home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 100 / CD-ROM 100.iso / aplic / concalc14 / concalc.exe / {app} / concalc14.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-12-20  |  86.7 KB  |  3,299 lines

  1. //concalc version 1.4,
  2. //written by Tarvo Korrovits in December, 2001,
  3. //original version made by Varun Ranjit Singh,
  4. //distributed under GNU General Public License
  5. //(there is no warranty).
  6. //"Do not create console" unchecked in Dev-C++ project
  7. //options, libcomdlg32.a shall be in object files box.
  8. //http://sourceforge.net/projects/tkorrovi
  9.  
  10. #include <windows.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <math.h>
  14.  
  15. enum COLORS {BLACK, BLUE, GREEN, CYAN, RED, MAGENTA,
  16.   BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, LIGHTGREEN,
  17.   LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE};
  18.  
  19. #define TEXTCOLOR BLACK + (WHITE << 4)
  20. #define ERRORCOLOR RED + (WHITE << 4)
  21. #define VALUECOLOR CYAN + (WHITE << 4)
  22. #define FORMULACOLOR MAGENTA + (WHITE << 4)
  23. #define BLANKCOLOR WHITE + (WHITE << 4)
  24. #define HEADERCOLOR WHITE + (CYAN << 4)
  25. #define HIGHLIGHTCOLOR WHITE + (DARKGRAY << 4)
  26. #define HIGHLIGHTERRORCOLOR WHITE + (RED << 4)
  27. #define MSGAUTOCALCCOLOR CYAN + (WHITE << 4)
  28. #define MSGFORMDISPLAYCOLOR MAGENTA + (WHITE << 4)
  29. #define MSGMEMORYCOLOR GREEN + (WHITE << 4)
  30. #define MSGHEADERCOLOR CYAN + (WHITE << 4)
  31. #define PROMPTCOLOR BROWN + (WHITE << 4)
  32. #define COMMANDCOLOR CYAN + (WHITE << 4)
  33. #define LOWCOMMANDCOLOR BLACK + (WHITE << 4)
  34. #define MEMORYCOLOR RED + (WHITE << 4)
  35. #define CELLTYPECOLOR GREEN + (WHITE << 4)
  36. #define CELLCONTENTSCOLOR BROWN + (WHITE << 4)
  37. #define MSGTITLE "concalc 1.4, distributed under GNU General Public License (no warranty)"
  38. #define MSGABOUT1 "Modified, copying and tsv added by Tarvo Korrovits.\n"
  39. #define MSGABOUT2 "originally made by Varun Ranjit Singh"
  40. #define MSGKEYPRESS "Press any key to continue."
  41. #define MSGCOMMAND "Press / for the list of commands"
  42. #define MSGMEMORY "Memory Available:"
  43. #define MSGERROR "ERROR"
  44. #define MSGLOMEM "Not enough memory to allocate cell."
  45. #define MSGEMPTY "Empty"
  46. #define MSGTEXT  "Text"
  47. #define MSGVALUE "Value"
  48. #define MSGFORMULA "Formula"
  49. #define MSGAUTOCALC "AutoCalc"
  50. #define MSGFORMDISPLAY "Form"
  51. #define MSGFILENAME "Enter the file name of the spreadsheet:"
  52. #define MSGCOLWIDTH "Enter the new column width:"
  53. #define MSGNOOPEN "Can't open the file."
  54. #define MSGOVERWRITE "The file exists.  Do you want to overwrite it?"
  55. #define MSGFILELOMEM "Not enough memory for entire spreadsheet."
  56. #define MSGNOEXIST "The file does not exist."
  57. #define MSGGOTO "Enter the cell to go to:"
  58. #define MSGBADNUMBER "You must enter a number from %d to %d."
  59. #define MSGBADCELL "That is not a legal cell."
  60. #define MSGRIGHTJUST "Do you want the cell right-justified?"
  61. #define MSGDOLLAR "Do you want numbers in a dollar format?"
  62. #define MSGCOMMAS "Do you want commas in numbers?"
  63. #define MSGPLACES "How many decimal places should the number be rounded to?"
  64. #define MSGCOLUMNS "Do you want to print in 132 columns?"
  65. #define MSGBORDER "Print the border?"
  66. #define MSGLOADING "Loading..."
  67. #define MSGSAVING "Saving..."
  68. #define MSGSAVESHEET "Save current spreadsheet?"
  69. #define MSGSTACKERROR "Parser stack overflow."
  70. #define MSGIOERROR "i/o error"
  71. #define MENU "Spreadsheet, Format, Delete, Goto, Col, Row, Edit, Utility, Auto, Quit"
  72. #define COMMAND "SFDGCREUAQ"
  73. #define SMENU "Load, Save, Print, Clear, About"
  74. #define SCOMMAND "LSPCA"
  75. #define CMENU "Insert, Delete, Width"
  76. #define CCOMMAND "IDW"
  77. #define RMENU "Insert, Delete"
  78. #define RCOMMAND "ID"
  79. #define UMENU "Recalc, Formula display"
  80. #define UCOMMAND "RF"
  81. #define S_IREAD     0x0100    
  82. #define S_IWRITE 0x0080    
  83. #define TRUE 1
  84. #define FALSE 0
  85. #define DOUBLE double
  86. #define MAXCOLS 100
  87. #define MAXROWS 999
  88. #define LEFTMARGIN 3
  89. #define MINCOLWIDTH 3
  90. #define MAXCOLWIDTH 80 - LEFTMARGIN
  91. #define SCREENCOLS (80 - LEFTMARGIN) / MINCOLWIDTH + 1
  92. #define SCREENROWS 20
  93. #define DEFAULTWIDTH 10
  94. #define DEFAULTFORMAT 0X42
  95. #define MAXINPUT 79
  96. #define MAXPLACES 8
  97. #define TOPMARGIN 5
  98. #define PARSERSTACKSIZE 200
  99. #define HIGHLIGHT TRUE
  100. #define NOHIGHLIGHT FALSE
  101. #define UPDATE TRUE
  102. #define NOUPDATE FALSE
  103. #define FORMAT TRUE
  104. #define NOFORMAT FALSE
  105. #define LEFT 0
  106. #define RIGHT 1
  107. #define UP 2
  108. #define DOWN 3
  109. #define TEXTCELL 0
  110. #define VALUE 1
  111. #define FORMULA 2
  112. #define COLADD 0
  113. #define COLDEL 1
  114. #define ROWADD 2
  115. #define ROWDEL 3
  116. #define OVERWRITE 0X80
  117. #define RJUSTIFY 0X40
  118. #define COMMAS 0X20
  119. #define DOLLAR 0X10
  120. #define BS 8
  121. #define FORMFEED 12
  122. #define CR 13
  123. #define ESC 27
  124. #define HOMEKEY 327
  125. #define ENDKEY 335
  126. #define UPKEY 328
  127. #define DOWNKEY 336
  128. #define PGUPKEY 329
  129. #define PGDNKEY 337
  130. #define LEFTKEY 331
  131. #define INSKEY 338
  132. #define RIGHTKEY 333
  133. #define DELKEY 339
  134. #define CTRLLEFTKEY 371
  135. #define CTRLRIGHTKEY 372
  136. #define F9 315
  137. #define F2 316
  138. #define SHIFTUPKEY 401
  139. #define SHIFTDOWNKEY 402
  140. #define SHIFTLEFTKEY 403
  141. #define SHIFTRIGHTKEY 404
  142. #define CTRLC 405
  143. #define CTRLV 406
  144. #define ALTF4 407
  145. #define F4 408
  146. #define TABKEY 409
  147. #define SHIFTENDKEY 410
  148. #define SHIFTF9 411
  149. #define PLUS 0
  150. #define MINUS 1
  151. #define TIMES 2
  152. #define DIVIDE 3
  153. #define EXP 4
  154. #define COLON 5
  155. #define OPAREN 6
  156. #define CPAREN 7
  157. #define NUM 8
  158. #define CELL 9
  159. #define FUNC 10
  160. #define EOLN 11
  161. #define BAD 12
  162. #define MAXFUNCNAMELEN 5
  163. #define RPLUS 1
  164. #define RMINUS 2
  165. #define RTIMES 4
  166. #define RDIVIDE 5
  167. #define RPOW 7
  168. #define RNEGATE 9
  169. #define RSUM 11
  170. #define RCELLVALUE 13
  171. #define RFUNC 16
  172. #define RNUM 15
  173. #define RCPAREN 14
  174. #define RFIRSTPOW 6
  175. #define RFIRSTNEGATE 8
  176. #define RFIRSTSUM 10
  177. #define RFIRSTFUNC 12
  178. #define RFIRSTTIMES 3
  179. #define SNUM 10
  180. #define SCELL 7
  181. #define SFUNC 11
  182. #define SNEGATE 5
  183. #define SOPAREN 9
  184. #define SPLUS 12
  185. #define SMINUS 13
  186. #define STIMES 14
  187. #define SDIVIDE 15
  188. #define SFIRST 0
  189. #define SPOW 16
  190. #define SCOLON 18
  191. #define SFOPAREN 20
  192. #define SDONEGATE 17
  193. #define SDOTIMES 23
  194. #define SDODIVIDE 24
  195. #define SDOPOW 25
  196. #define SDOSUM 26
  197. #define SDOFUNC 29
  198. #define SERROR 30
  199. #define SCPAREN 27
  200. #define SFIRSTPLUS 1
  201. #define SOPARENPLUS 19
  202. #define SFOPARENPLUS 28
  203. #define SFIRSTTIMES 2
  204. #define SFIRSTPOW 3
  205. #define SFIRSTNEGATE 4
  206. #define SFIRSTSUM 6
  207. #define SPLUSTIMES 21
  208. #define SMINUSTIMES 22
  209. #define SFIRSTFUNC 8
  210.  
  211. struct CELLREC
  212. {
  213.   CHAR attrib;
  214.   union
  215.   {
  216.     CHAR text [MAXINPUT + 1];
  217.     DOUBLE value;
  218.     struct
  219.     {
  220.       DOUBLE fvalue;
  221.       CHAR formula [MAXINPUT + 1];
  222.     } f;
  223.   } v;
  224. };
  225.  
  226. typedef struct CELLREC *CELLPTR;
  227.  
  228. struct TOKENREC
  229. {
  230.   CHAR state;
  231.   union
  232.   {
  233.     DOUBLE value;
  234.     struct
  235.     {
  236.       INT row, col;
  237.     } c;
  238.     CHAR funcname [MAXFUNCNAMELEN + 1];
  239.   } x;
  240. };
  241.  
  242. INT getkey (VOID);
  243. INT editstring (LPSTR s, LPSTR legal, INT maxlength);
  244. INT getint (LPINT number, INT low, INT high);
  245. VOID scroll (INT direction, INT lines, INT x1, INT y1, 
  246.   INT x2, INT y2, INT attrib);
  247. VOID setcursor (UINT shape);
  248. VOID writef (INT col, INT row, INT color, INT width, 
  249.   LPSTR format, ...);
  250. VOID printcol (VOID);
  251. VOID displaycell (INT col, INT row, INT highlighting, 
  252.   INT updating);
  253. VOID displaycol (INT col, INT updating);
  254. VOID displayrow (INT row, INT updating);
  255. VOID displayscreen (INT updating);
  256. VOID showcelltype (VOID);
  257. DOUBLE parse (LPSTR s, LPINT att);
  258. INT alloctext (INT col, INT row, LPSTR s);
  259. INT allocvalue (INT col, INT row, DOUBLE amt);
  260. INT allocformula (INT col, INT row, LPSTR s, DOUBLE amt);
  261. VOID deletecell (INT col, INT row, INT display);
  262. VOID printfreemem (VOID);
  263. VOID moverowup (VOID);
  264. VOID moverowdown (VOID);
  265. VOID movecolleft (VOID);
  266. VOID movecolright (VOID);
  267. VOID recalc (VOID);
  268. VOID changeautocalc (INT newmode);
  269. VOID changeformdisplay (INT newmode);
  270. VOID errormsg (LPSTR s);
  271. VOID colstring (INT col, LPSTR colstr);
  272. VOID centercolstring (INT col, LPSTR colstr);
  273. VOID setleftcol (VOID);
  274. VOID setrightcol (VOID);
  275. VOID setbottomrow (VOID);
  276. VOID setlastcol (VOID);
  277. VOID setlastrow (VOID);
  278. VOID act (LPSTR s);
  279. VOID initvars (VOID);
  280. INT getcommand (LPSTR msgstr, LPSTR comstr);
  281. VOID mainmenu (VOID);
  282. VOID editcell (CELLPTR ecell);
  283. INT setoflags (INT col, INT row, INT display);
  284. VOID clearoflags (INT col, INT row, INT display);
  285. VOID updateoflags (INT col, INT row, INT display);
  286. VOID loadsheet (BOOL comline);
  287. INT getcell (LPINT col, LPINT row);
  288. LPSTR cellstring (INT col, INT row, LPINT color, 
  289.   INT formatting);
  290. INT getyesno (LPINT yesno, LPSTR prompt);
  291. VOID savesheet (VOID);
  292. INT formulastart (LPSTR *input, LPINT col, LPINT row);
  293. INT rowwidth (INT row);
  294. VOID fixformula (INT col, INT row, INT action, 
  295.   INT place);
  296. VOID clearlastcol (VOID);
  297. VOID setrect (INT left, INT top, INT right, INT bottom);
  298. VOID movetext (INT left, INT top, INT right, INT bottom,
  299.   INT newleft, INT newtop);
  300. BOOL iscorrectkey (VOID);
  301. VOID fixformula2 (INT col, INT row, INT coldif, 
  302.   INT rowdif);
  303. VOID shift (INT state);
  304. VOID reduce (INT reduction);
  305. VOID printsheet (VOID);
  306. VOID clearsheet (VOID);
  307. VOID formatcells (VOID);
  308. VOID insertcol (INT col);
  309. VOID deletecol (INT col);
  310. VOID setcolwidth (INT col);
  311. VOID insertrow (INT row);
  312. VOID deleterow (INT row);
  313. VOID textstring (LPSTR instring, LPSTR outstring, 
  314.   INT col, INT fvalue, INT formatting);
  315. VOID valuestring (CELLPTR cellptr, DOUBLE value, 
  316.   LPSTR vstring, INT col, INT fvalue, LPINT color, 
  317.   INT formatting);
  318. DOUBLE cellvalue (INT col, INT row);
  319. INT isfunc (LPSTR s);
  320. VOID addformula (VOID);
  321. UINT APIENTRY hookproc1 (HWND hdlg, UINT uimsg, 
  322.   WPARAM wparam, LPARAM lparam);
  323. UINT APIENTRY hookproc2 (HWND hdlg, UINT uimsg, 
  324.   WPARAM wparam, LPARAM lparam);
  325. INT copys (VOID);
  326. VOID pastes (VOID);
  327. INT pagerows (INT row, INT toppage, INT border);
  328. INT pagecols (INT col, INT border, INT columns);
  329.  
  330. INT leftcol, rightcol, toprow, bottomrow, curcol, inpt,
  331.   currow, lastcol, lastrow, curcolb, currowb, i, j, 
  332.   leftcolb, toprowb, loading, stacktop, tokentype, 
  333.   error, counter, col, row, oldleftcol, oldrightcol, 
  334.   size, allocated, n, reallastcol, reallastrow, ignorep,
  335.   overwrite, cl, rw, rows, len, firstcol, columns, 
  336.   counter1, counter2, counter3, border, toppage, lcol, 
  337.   lrow, dummy, printed, oldlastcol, width, col1, col2,
  338.   row1, temp, newformat, rowc, color, pos, insert, row2,
  339.   good, first, oldcol, oldrow, numlen, decimal, maxlen, 
  340.   fcol, frow, lrowdif, spaces1, spaces2, total, attrib, 
  341.   newcol, formatvalue, ch, changed, formdisplay, ofd, 
  342.   autocalc, stop, correctkey, success, isformula, 
  343.   accepted, ii, jj, coldif, rowdif, ocurcol, ocurrow;
  344. CHAR str [MAXINPUT + 1], filename [MAX_PATH],
  345.   colstr [MAXINPUT + 1], data [10], pfilename [10], 
  346.   numstring [MAXINPUT + 1], copy [MAXINPUT + 1], 
  347.   newformula [MAXINPUT + 1], just [10], ljust [10], 
  348.   rjust [10], tmp [MAXINPUT + 1], cstr [MAXINPUT + 1],
  349.   stri [MAXINPUT + 1], ffstr [MAXINPUT + 1];
  350. UCHAR format [MAXCOLS] [MAXROWS], colwidth [MAXCOLS],
  351.   colstart [SCREENCOLS], oldcolstart [SCREENCOLS];
  352. LPSTR input, ftext, fstr, s, start, clstart,
  353.   rowstart, curpos, fstring, p, *filepart;
  354. DWORD shortcursor = 0x0607, tallcursor = 0x0507, 
  355.   nocursor = 0x2000, c, ac, outc, written, fsize;
  356. DOUBLE value;
  357. CELLPTR cell [MAXCOLS] [MAXROWS], curcell, cellptr1, 
  358.   cellptr2, cellptr;
  359. TOKENREC stack [PARSERSTACKSIZE], curtoken, token1, 
  360.   token2;
  361. va_list arg_ptr;
  362. MEMORYSTATUS memstatus;
  363. CONSOLE_CURSOR_INFO curinfo;
  364. INPUT_RECORD inp;
  365. HANDLE si, so, hfile;
  366. COORD coord;
  367. CHAR_INFO charinfo;
  368. SMALL_RECT R;
  369. OPENFILENAME ofn;
  370. HGLOBAL ctext;
  371. HWND hwndmain;
  372.  
  373. //checks whether pressed key have to be processed
  374. BOOL iscorrectkey (VOID)
  375. {
  376.   c = inp.Event.KeyEvent.wVirtualKeyCode;
  377.   correctkey = FALSE;
  378.   if (inp.Event.KeyEvent.bKeyDown == TRUE && c != 16)
  379.   {
  380.     if (inp.Event.KeyEvent.uChar.AsciiChar != 0)
  381.       correctkey = TRUE;
  382.     else if ((c >= 33 && c <= 40) || c == 110 ||
  383.       c == 45 || c == 120 || c == 113 || c == 67 ||
  384.       c == 86 || c == 115 || c == 191)
  385.       correctkey = TRUE;
  386.   }
  387.   return (correctkey);
  388. }
  389.  
  390. //gets key and removes input message
  391. INT getkey (VOID)
  392. {
  393.   do
  394.     ReadConsoleInput (si, &inp, 1, (LPDWORD) &written);
  395.   while (iscorrectkey () == FALSE);
  396.   c = inp.Event.KeyEvent.wVirtualKeyCode;
  397.   ac = inp.Event.KeyEvent.uChar.AsciiChar;
  398.   outc = 0;
  399.   if (ac != 0 && c != 110 && c != 67 && c != 86 && 
  400.     c != 9)
  401.     return (ac);
  402.   if (c == 36) outc = HOMEKEY;
  403.   if (c == 35) outc = ENDKEY;
  404.   if (c == 38) outc = UPKEY;
  405.   if (c == 40) outc = DOWNKEY;
  406.   if (c == 33) outc = PGUPKEY;
  407.   if (c == 34) outc = PGDNKEY;
  408.   if (c == 37) outc = LEFTKEY;
  409.   if (c == 110) outc = DELKEY;
  410.   if (c == 45) outc = INSKEY;
  411.   if (c == 39) outc = RIGHTKEY;
  412.   if (c == 120) outc = F9;
  413.   if (c == 113) outc = F2;
  414.   if (c == 9) outc = TABKEY;
  415.   if (c == 191) outc = '^';
  416.   if (c == 115 && 
  417.     ((inp.Event.KeyEvent.dwControlKeyState |  
  418.     RIGHT_ALT_PRESSED) ==
  419.     inp.Event.KeyEvent.dwControlKeyState || 
  420.     (inp.Event.KeyEvent.dwControlKeyState |  
  421.     LEFT_ALT_PRESSED) ==
  422.     inp.Event.KeyEvent.dwControlKeyState)) 
  423.     outc = ALTF4;
  424.   else if (c == 115)
  425.     outc = F4;
  426.   if (c == 67 && 
  427.     ((inp.Event.KeyEvent.dwControlKeyState |  
  428.     RIGHT_CTRL_PRESSED) ==
  429.     inp.Event.KeyEvent.dwControlKeyState || 
  430.     (inp.Event.KeyEvent.dwControlKeyState |  
  431.     LEFT_CTRL_PRESSED) ==
  432.     inp.Event.KeyEvent.dwControlKeyState)) 
  433.     outc = CTRLC;
  434.   else if (c == 67)
  435.     outc = ac;
  436.   if (c == 86 && 
  437.     ((inp.Event.KeyEvent.dwControlKeyState |  
  438.     RIGHT_CTRL_PRESSED) ==
  439.     inp.Event.KeyEvent.dwControlKeyState || 
  440.     (inp.Event.KeyEvent.dwControlKeyState |  
  441.     LEFT_CTRL_PRESSED) ==
  442.     inp.Event.KeyEvent.dwControlKeyState)) 
  443.     outc = CTRLV;
  444.   else if (c == 86)
  445.     outc = ac;
  446.   if (c == 37 && 
  447.     ((inp.Event.KeyEvent.dwControlKeyState |  
  448.     RIGHT_CTRL_PRESSED) ==
  449.     inp.Event.KeyEvent.dwControlKeyState || 
  450.     (inp.Event.KeyEvent.dwControlKeyState |  
  451.     LEFT_CTRL_PRESSED) ==
  452.     inp.Event.KeyEvent.dwControlKeyState)) 
  453.     outc = CTRLLEFTKEY;
  454.   if (c == 39 && 
  455.     ((inp.Event.KeyEvent.dwControlKeyState |  
  456.     RIGHT_CTRL_PRESSED) ==
  457.     inp.Event.KeyEvent.dwControlKeyState || 
  458.     (inp.Event.KeyEvent.dwControlKeyState |  
  459.     LEFT_CTRL_PRESSED) ==
  460.     inp.Event.KeyEvent.dwControlKeyState)) 
  461.     outc = CTRLRIGHTKEY;
  462.   if (c == 37 && 
  463.     (inp.Event.KeyEvent.dwControlKeyState |  
  464.     SHIFT_PRESSED) ==
  465.     inp.Event.KeyEvent.dwControlKeyState)
  466.     outc = SHIFTLEFTKEY;
  467.   if (c == 39 && 
  468.     (inp.Event.KeyEvent.dwControlKeyState |  
  469.     SHIFT_PRESSED) ==
  470.     inp.Event.KeyEvent.dwControlKeyState)
  471.     outc = SHIFTRIGHTKEY;
  472.   if (c == 38 && 
  473.     (inp.Event.KeyEvent.dwControlKeyState |  
  474.     SHIFT_PRESSED) ==
  475.     inp.Event.KeyEvent.dwControlKeyState)
  476.     outc = SHIFTUPKEY;
  477.   if (c == 40 && 
  478.     (inp.Event.KeyEvent.dwControlKeyState |  
  479.     SHIFT_PRESSED) ==
  480.     inp.Event.KeyEvent.dwControlKeyState)
  481.     outc = SHIFTDOWNKEY;
  482.   if (c == 35 && 
  483.     (inp.Event.KeyEvent.dwControlKeyState |  
  484.     SHIFT_PRESSED) ==
  485.     inp.Event.KeyEvent.dwControlKeyState)
  486.     outc = SHIFTENDKEY;
  487.   if (c == 120 && 
  488.     (inp.Event.KeyEvent.dwControlKeyState |  
  489.     SHIFT_PRESSED) ==
  490.     inp.Event.KeyEvent.dwControlKeyState)
  491.     outc = SHIFTF9;
  492.   return (outc);
  493. }
  494.  
  495. //prints string on console
  496. VOID writef (INT col, INT row, INT color, INT width, 
  497.   LPSTR format, ...)
  498. {
  499.   va_start (arg_ptr, format);
  500.   wvsprintf (tmp, format, arg_ptr);
  501.   tmp [width] = 0;
  502.   if ((n = lstrlen (tmp)) < width)
  503.     FillMemory (&tmp [n], width - n, ' ');
  504.   for (i = 0; i <= lstrlen (tmp); i++)
  505.     if (tmp [i] == (CHAR) 228 || tmp [i] == (CHAR) 229)
  506.       tmp [i] = 147;
  507.   SetConsoleTextAttribute (so, color);
  508.   SetConsoleCursorPosition (so, 
  509.     (COORD) {col - 1, row - 1});
  510.   WriteConsole (so, tmp, lstrlen (tmp), &written, NULL);
  511. }
  512.  
  513. //moves a rectangular area of the screen
  514. VOID movetext (INT left, INT top, INT right, INT bottom,
  515.   INT newleft, INT newtop)
  516. {
  517.   R.Left = left - 1;
  518.   R.Top = top - 1;
  519.   R.Right = right - 1;
  520.   R.Bottom = bottom - 1;
  521.   ScrollConsoleScreenBuffer (so, &R, NULL,
  522.     (COORD) {newleft - 1, newtop - 1}, &charinfo);
  523. }
  524.  
  525. //sets small rectangle
  526. VOID setrect (INT left, INT top, INT right, INT bottom)
  527. {
  528.   R.Left = left;
  529.   R.Top = top;
  530.   R.Right = right;
  531.   R.Bottom = bottom;
  532. }
  533.  
  534. //scrolls an area of the screen
  535. VOID scroll (INT direction, INT lines, INT x1, INT y1, 
  536.   INT x2, INT y2, INT attrib)
  537. {
  538.   if (lines == 0)
  539.     setrect (x1, y1, x2, y2);
  540.   else
  541.     switch (direction)
  542.     {
  543.       case UP:
  544.         movetext (x1, y1 + lines, x2, y2, x1, y1);
  545.         setrect (x1, y2 - lines + 1, x2, y2);
  546.       break;
  547.       
  548.       case DOWN:
  549.         movetext (x1, y1, x2, y2 - lines, x1, y1 + lines);
  550.         setrect (x1, y1, x2, y1 + lines - 1);
  551.       break;
  552.       
  553.       case LEFT:
  554.         movetext (x1 + lines, y1, x2, y2, x1, y1);
  555.         setrect (x2 - lines + 1, y1, x2, y2);
  556.       break;
  557.       
  558.       case RIGHT:
  559.         movetext (x1, y1, x2 - lines, y2, x1 + lines, y1);
  560.         setrect (x1, y1, x1 + lines - 1, y2);
  561.       break;
  562.     }
  563.   coord.X = R.Left - 1;
  564.   for (coord.Y = R.Top - 1; coord.Y <= R.Bottom - 1; 
  565.     coord.Y++)
  566.     FillConsoleOutputAttribute 
  567.       (so, attrib, R.Right - R.Left + 1, coord, &written);
  568.   for (coord.Y = R.Top - 1; coord.Y <= R.Bottom - 1; 
  569.     coord.Y++)
  570.     FillConsoleOutputCharacter 
  571.       (so, ' ', R.Right - R.Left + 1, coord, &written);
  572. }
  573.  
  574. //creates output string for data in cell (col, row),
  575. //also returns the color of the cell
  576. LPSTR cellstring (INT col, INT row, LPINT color, 
  577.   INT formatting)
  578. {
  579.   cellptr = cell [col] [row];
  580.   if (cellptr == NULL)
  581.   {
  582.     if (!formatting || format [col] [row] < OVERWRITE)
  583.     {
  584.       sprintf (cstr, "%*s", colwidth [col], "");
  585.       *color = BLANKCOLOR;
  586.     }
  587.     else
  588.     {
  589.       newcol = col;
  590.       while (cell [--newcol] [row] == NULL);
  591.       p = cell [newcol] [row]->v.text;
  592.       while (newcol < col) p += colwidth [newcol++];
  593.       strncpy (tmp, p, colwidth [col]);
  594.       tmp [colwidth [col]] = 0;
  595.       sprintf (cstr, "%s%*s", tmp, colwidth [col] - 
  596.         lstrlen (tmp), "");
  597.       *color = TEXTCOLOR;
  598.     }
  599.   }
  600.   else
  601.   {
  602.     formatvalue = format [col] [row];
  603.     switch (cellptr->attrib)
  604.     {
  605.       case TEXTCELL:
  606.         textstring (cellptr->v.text, cstr, col, 
  607.           formatvalue, formatting);
  608.         *color = TEXTCOLOR;
  609.       break;
  610.       
  611.       case FORMULA:
  612.         if (formdisplay)
  613.         {
  614.           textstring (cellptr->v.f.formula, cstr, col, 
  615.             formatvalue, formatting);
  616.           *color = FORMULACOLOR;
  617.           break;
  618.         }
  619.         else
  620.           value = cellptr->v.f.fvalue;
  621.           
  622.       case VALUE :
  623.         if (cellptr->attrib == VALUE) 
  624.           value = cellptr->v.value;
  625.           valuestring (cellptr, value, cstr, col, 
  626.             formatvalue, color, formatting);
  627.       break;
  628.     }
  629.   }
  630.   return (cstr);
  631. }
  632.  
  633. //sets the string representation of a value
  634. VOID valuestring (CELLPTR cellptr, DOUBLE value, 
  635.   LPSTR vstring, INT col, INT fvalue, LPINT color, 
  636.   INT formatting)
  637. {
  638.   if (value == HUGE_VAL)
  639.   {
  640.     strcpy (vstring, MSGERROR);
  641.     *color = ERRORCOLOR;
  642.   }
  643.   else
  644.   {
  645.     if (formatting)
  646.     {
  647.       sprintf (vstring, "%1.*f", fvalue & 15, 
  648.         cellptr->v.value);
  649.       if (fvalue & COMMAS)
  650.       {
  651.         pos = strcspn (vstring, ".");
  652.         while (pos > 3)
  653.         {
  654.           pos -= 3;
  655.           if (vstring [pos - 1] != '-')
  656.           {
  657.             MoveMemory (&vstring[pos + 1], &vstring[pos], 
  658.               lstrlen (vstring) - pos + 1);
  659.             vstring [pos] = ',';
  660.           }
  661.         }
  662.       }
  663.       if (fvalue & DOLLAR)
  664.       {
  665.         if (vstring [0] == '-')
  666.         {
  667.           fstring = " $";
  668.           width = colwidth [col] - 2;
  669.         }
  670.         else
  671.         {
  672.           fstring = " $ ";
  673.           width = colwidth [col] - 3;
  674.         }
  675.       }
  676.       else
  677.       {
  678.         fstring = "";
  679.         width = colwidth [col];
  680.       }
  681.       strcpy (str, vstring);
  682.       if (fvalue & RJUSTIFY)
  683.       {
  684.         if (lstrlen (vstring) > width)
  685.           vstring [width] = 0;
  686.         else
  687.           sprintf (vstring, "%*s", width, str);
  688.       }
  689.       else
  690.         sprintf (vstring, "%-*s", width, s);
  691.       MoveMemory (&vstring [lstrlen (fstring)], vstring, 
  692.         lstrlen (vstring) + 1);
  693.       strncpy (vstring, fstring, lstrlen (fstring));
  694.     }
  695.     else
  696.       sprintf (vstring, "%.*f", MAXPLACES, value);
  697.     *color = VALUECOLOR;
  698.   }
  699. }
  700.  
  701. //sets the string representation of text
  702. VOID textstring (LPSTR instring, LPSTR outstring, 
  703.   INT col, INT fvalue, INT formatting)
  704. {
  705.   lstrcpy (ljust, "%-*s");
  706.   lstrcpy (rjust, "%*s");
  707.   if ((fvalue & RJUSTIFY) && formatting)
  708.     lstrcpy (just, rjust);
  709.   else
  710.     lstrcpy (just, ljust);
  711.   sprintf (outstring, just, colwidth [col], instring);
  712.   if (formatting) outstring [colwidth [col]] = 0;
  713. }
  714.  
  715. //sets overwrite flag on cells starting at (col + 1, 
  716. //row), returns number of column after last column set
  717. INT setoflags (INT col, INT row, INT display)
  718. {
  719.   len = lstrlen (cell [col] [row]->v.text) - 
  720.     colwidth [col];
  721.   while (++col < MAXCOLS && len > 0 && 
  722.     cell [col] [row] == NULL)
  723.   {
  724.     format [col] [row] |= OVERWRITE;
  725.     len -= colwidth [col];
  726.     if (display && col >= leftcol && col <= rightcol)
  727.       displaycell (col, row, NOHIGHLIGHT, NOUPDATE);
  728.   }
  729.   return (col);
  730. }
  731.  
  732. //moves back from col to last TEXTCELL and updates flags
  733. VOID updateoflags (INT col, INT row, INT display)
  734. {
  735.   while (cell [col] [row] == NULL && col-- > 0);
  736.   if (col >= 0)
  737.     if (cell [col] [row] != NULL && 
  738.       cell [col] [row]->attrib == TEXTCELL)
  739.       setoflags (col, row, display);
  740. }
  741.  
  742. //clears overwrite flag on cells starting at (col, row)
  743. VOID clearoflags (INT col, INT row, INT display)
  744. {
  745.   while (format [col] [row] >= OVERWRITE && 
  746.     col < MAXCOLS && cell [col] [row] == NULL)
  747.   {
  748.     format [col] [row] &= ~OVERWRITE;
  749.     if (display && col >= leftcol &&  col <= rightcol)
  750.       displaycell (col, row, NOHIGHLIGHT, NOUPDATE);
  751.     col++;
  752.   }
  753. }
  754.  
  755. //acts on a particular input
  756. VOID act (LPSTR s)
  757. {
  758.   deletecell (curcol, currow, UPDATE);
  759.   value = parse (s, &attrib);
  760.   switch (attrib)
  761.   {
  762.     case TEXTCELL:
  763.       allocated = alloctext (curcol, currow, s);
  764.       if (allocated) 
  765.         displaycell (curcol, currow, NOHIGHLIGHT, 
  766.           NOUPDATE);
  767.     break;
  768.     
  769.     case VALUE: 
  770.       allocated = allocvalue (curcol, currow, value); 
  771.     break;
  772.     
  773.     case FORMULA:
  774.       allocated = allocformula (curcol, currow, s, value);
  775.     break;
  776.   }
  777.   if (allocated)
  778.   {
  779.     format [curcol] [currow] &= ~OVERWRITE;
  780.     clearoflags (curcol + 1, currow, UPDATE);
  781.     if (attrib == TEXTCELL) 
  782.       setoflags (curcol, currow, UPDATE);
  783.     if (curcol > lastcol) lastcol = curcol;
  784.     if (currow > lastrow) lastrow = currow;
  785.     if (autocalc) recalc ();
  786.   }
  787.   else
  788.     errormsg (MSGLOMEM);
  789.   printfreemem ();
  790. }
  791.  
  792. //sets value of lastrow based on the current value
  793. VOID setlastrow (VOID)
  794. {
  795.   for (row = lastrow; row >= 0; row--)
  796.     for (col = 0; col <= lastcol; col++)
  797.       if (cell [col] [row] != NULL)
  798.       {
  799.         lastrow = row;
  800.         return;
  801.       }
  802.   lastrow = 0;
  803. }
  804.  
  805. //sets value of lastcol based on the current value
  806. VOID setlastcol (VOID)
  807. {
  808.   for (col = lastcol; col >= 0; col--)
  809.     for (row = 0; row <= lastrow; row++)
  810.       if (cell [col] [row] != NULL)
  811.       {
  812.         lastcol = col;
  813.         return;
  814.       }
  815.   lastcol = 0;
  816. }
  817.  
  818. //figures out value of bottomrow based on value of toprow
  819. VOID setbottomrow (VOID)
  820. {
  821.   if (toprow + SCREENROWS > MAXROWS) 
  822.     toprow = MAXROWS - 20;
  823.   bottomrow = toprow + 19;
  824.   for (row = 0; row < SCREENROWS; row++)
  825.     writef (1, row + 3, HEADERCOLOR, LEFTMARGIN, "%-d", 
  826.       row + toprow + 1);
  827. }
  828.  
  829. //sets value of rightcol based on the value of leftcol
  830. VOID setrightcol (VOID)
  831. {
  832.   total = LEFTMARGIN;
  833.   col = 0;
  834.   do
  835.   {
  836.     colstart [col] = total;
  837.     total += colwidth [leftcol + col++];
  838.   } 
  839.   while (total <= 80 && leftcol + col <= MAXCOLS);
  840.   rightcol = leftcol + col - 2;
  841.   printcol ();
  842. }
  843.  
  844. //sets value of leftcol based on the value of rightcol
  845. VOID setleftcol (VOID)
  846. {
  847.   total = 80;
  848.   col = 0;
  849.   while (total >= LEFTMARGIN && rightcol - col >= 0)
  850.   {
  851.     colstart [SCREENCOLS - col - 1] = 
  852.       total - colwidth [rightcol - col];
  853.     total -= colwidth [rightcol - col++];
  854.   }
  855.   if (total >= LEFTMARGIN) col++;
  856.   MoveMemory (colstart, &colstart [SCREENCOLS - col + 1], 
  857.     col - 1);
  858.   leftcol = rightcol - col + 2;
  859.   total = colstart [0] - LEFTMARGIN;
  860.   if (total != 0)
  861.     for (col = leftcol; col <= rightcol; col++)
  862.       colstart [col - leftcol] -= total;
  863.   printcol();
  864. }
  865.  
  866. //changes a column to a centered string
  867. VOID centercolstring (INT col, LPSTR colstr)
  868. {
  869.   colstring (col, str);
  870.   spaces1 = (colwidth [col] - lstrlen (str)) >> 1;
  871.   spaces2 = colwidth [col] - lstrlen (str) - spaces1;
  872.   sprintf (colstr, "%*s%s%*s", spaces1, "", str, 
  873.     spaces2, "");
  874. }
  875.  
  876. //changes a column number to a string
  877. VOID colstring (INT col, LPSTR colstr)
  878. {
  879.   FillMemory (colstr, 3, 0);
  880.   if (col < 26)
  881.     colstr [0] = col + 'A';
  882.   else
  883.   {
  884.     colstr [0] = (col / 26) - 1 + 'A';
  885.     colstr [1] = (col % 26) + 'A';
  886.   }
  887. }
  888.  
  889. //fix copied formula
  890. VOID fixformula2 (INT col, INT row, INT acoldif, 
  891.   INT arowdif)
  892. {
  893.   coldif = acoldif;
  894.   rowdif = arowdif;
  895.   curpos = newformula;
  896.   cellptr = cell [col] [row];
  897.   strcpy (newformula, cellptr->v.f.formula);
  898.   while (*curpos != 0)
  899.   {
  900.     if (formulastart (&curpos, &fcol, &frow))
  901.     {
  902.       if (fcol + acoldif < 0) coldif = MAXCOLS + acoldif;
  903.       if (frow + arowdif < 0) rowdif = MAXROWS + arowdif;
  904.       if (fcol + acoldif >= MAXCOLS)
  905.         coldif = acoldif - MAXCOLS; 
  906.       if (frow + arowdif >= MAXROWS)
  907.         rowdif = arowdif - MAXROWS; 
  908.       rowstart = curpos - rowwidth (frow);
  909.       clstart = rowstart - ((fcol > 25) ? 2 : 1);
  910.       if (fcol <= 25 && fcol + coldif > 25)
  911.       {
  912.         if (lstrlen (newformula) == MAXINPUT)
  913.         {
  914.           deletecell (col, row, NOUPDATE);
  915.           alloctext (col, row, newformula);
  916.           return;
  917.         }
  918.         MoveMemory (clstart + 1, clstart, 
  919.           lstrlen (clstart) + 1);
  920.         curpos++;
  921.         rowstart++;
  922.       }
  923.       if (fcol > 25 && fcol + coldif <= 25)
  924.       {
  925.         MoveMemory (clstart, clstart + 1, 
  926.           lstrlen (clstart) + 1);
  927.         curpos--;
  928.         rowstart--;
  929.       }
  930.  
  931.       lrowdif = rowwidth (frow + rowdif) - 
  932.         rowwidth (frow);
  933.       if (lrowdif > 0)
  934.       {
  935.         if (lstrlen (newformula) + lrowdif > MAXINPUT)
  936.         {
  937.           deletecell (col, row, NOUPDATE);
  938.           alloctext (col, row, newformula);
  939.           return;
  940.         }
  941.         MoveMemory (rowstart + lrowdif, rowstart, 
  942.           lstrlen (rowstart) + 1);
  943.         curpos = curpos + lrowdif;
  944.       }
  945.       if (lrowdif < 0)
  946.       {
  947.         MoveMemory (rowstart, rowstart - lrowdif, 
  948.           lstrlen (rowstart) + 1);
  949.         curpos = curpos + lrowdif;
  950.       }
  951.            
  952.       colstring (fcol + coldif, ffstr);
  953.       MoveMemory (clstart, ffstr, lstrlen (ffstr));
  954.       sprintf (ffstr, "%d", frow + rowdif + 1);
  955.       MoveMemory (rowstart, ffstr, lstrlen (ffstr));
  956.     }
  957.     else
  958.       curpos++;
  959.   }
  960.   strcpy (cellptr->v.f.formula, newformula);
  961. }
  962.  
  963. //modifies column or row designations of the formula 
  964. VOID fixformula (INT col, INT row, INT action, INT place)
  965. {
  966.   s = str; 
  967.   curpos = newformula;
  968.   cellptr = cell [col] [row];
  969.   strcpy (newformula, cellptr->v.f.formula);
  970.   while (*curpos != 0)
  971.   {
  972.     if (formulastart (&curpos, &fcol, &frow))
  973.     {
  974.       rowstart = curpos - rowwidth (frow);
  975.       clstart = rowstart - ((fcol > 25) ? 2 : 1);
  976.       switch (action)
  977.       {
  978.         case COLADD:
  979.           if (fcol < place) break;
  980.           if (fcol == 25)
  981.           {
  982.             if (lstrlen (newformula) == MAXINPUT)
  983.             {
  984.               deletecell (col, row, NOUPDATE);
  985.               alloctext (col, row, newformula);
  986.               return;
  987.             }
  988.             MoveMemory (clstart + 1, clstart, 
  989.               lstrlen (clstart) + 1);
  990.           }
  991.           colstring (fcol + 1, s);
  992.           MoveMemory (clstart, s, lstrlen (s));
  993.         break;
  994.         
  995.         case ROWADD:
  996.           if (frow < place) break;
  997.           if (rowwidth (frow + 1) != rowwidth (frow))
  998.           {
  999.             if (lstrlen (newformula) == MAXINPUT)
  1000.             {
  1001.               deletecell (col, row, NOUPDATE);
  1002.               alloctext (col, row, newformula);
  1003.               return;
  1004.             }
  1005.             MoveMemory (rowstart + 1, rowstart, 
  1006.               lstrlen (rowstart) + 1);
  1007.           }
  1008.           sprintf (s, "%d", frow + 2);
  1009.           MoveMemory (rowstart, s, lstrlen (s));
  1010.         break;
  1011.         
  1012.         case COLDEL:
  1013.           if (fcol <= place) break;
  1014.           if (fcol == 26) 
  1015.             MoveMemory (clstart, clstart + 1, 
  1016.               lstrlen (clstart) + 1);
  1017.           colstring (fcol - 1, s);
  1018.           MoveMemory (clstart, s, lstrlen (s));
  1019.         break;
  1020.         
  1021.         case ROWDEL:
  1022.           if (frow <= place) break;
  1023.           if (rowwidth (frow) != rowwidth (frow - 1))
  1024.             MoveMemory (rowstart, rowstart + 1, 
  1025.               lstrlen (rowstart) + 1);
  1026.           sprintf (s, "%d", frow);
  1027.           MoveMemory (rowstart, s, lstrlen (s));
  1028.         break;
  1029.       }
  1030.     }
  1031.     else
  1032.       curpos++;
  1033.   }
  1034.   if (lstrlen (newformula) != 
  1035.     lstrlen (cellptr->v.f.formula))
  1036.   {
  1037.     value = cellptr->v.f.fvalue;
  1038.     deletecell (col, row, NOUPDATE);
  1039.     allocformula (col, row, newformula, value);
  1040.   }
  1041.   else
  1042.     strcpy (cellptr->v.f.formula, newformula);
  1043. }
  1044.  
  1045. VOID errormsg (LPSTR s)
  1046. {
  1047.   writef (1, 25, ERRORCOLOR, 79, "%s  %s", s, 
  1048.     MSGKEYPRESS);
  1049.   SetConsoleCursorPosition (so, (COORD) 
  1050.     {lstrlen (s) + lstrlen (MSGKEYPRESS) + 2, 24});
  1051.   getkey ();
  1052.   SetConsoleCursorPosition (so, (COORD) {0, 24});
  1053.   writef (1, 25, TEXTCOLOR, 79, "");
  1054. }
  1055.  
  1056. //returns TRUE if the string is the start of a formula, 
  1057. //also returns the column and row of the formula
  1058. int formulastart (char **input, int *col, int *row)
  1059. {
  1060.   maxlen = rowwidth (MAXROWS);
  1061.   if (!IsCharAlpha (**input)) return (FALSE);
  1062.   *col = *((*input)++) - 'A';
  1063.   if (IsCharAlpha (**input))
  1064.   {
  1065.     *col *= 26;
  1066.     *col += *((*input)++) - 'A' + 26;
  1067.   }
  1068.   if (*col >= MAXCOLS) return (FALSE);
  1069.   start = *input;
  1070.   for (len = 0; len < maxlen; len++)
  1071.   {
  1072.     if (**input < '0' || **input > '9') break;
  1073.     (*input)++;
  1074.   }
  1075.   if (len == 0) return (FALSE);
  1076.   strncpy (numstring, start, len);
  1077.   numstring [len] = 0;
  1078.   *row = atoi (numstring) - 1;
  1079.   if (*row >= MAXROWS || *row == -1) return (FALSE);
  1080.   return (TRUE);
  1081. }
  1082.  
  1083. //returns the width in spaces of row
  1084. INT rowwidth (INT row)
  1085. {
  1086.   return ((row == 0) ? 1 : (INT) log10 (row + 1) + 1);
  1087. }
  1088.  
  1089. VOID printfreemem (VOID)
  1090. {
  1091.   memstatus.dwLength = sizeof (MEMORYSTATUS);
  1092.   GlobalMemoryStatus (&memstatus);
  1093.   writef (lstrlen (MSGMEMORY) + 2, 1, MEMORYCOLOR, 10,
  1094.     "%lu", memstatus.dwAvailPhys);
  1095. }
  1096.  
  1097. VOID deletecell (INT col, INT row, INT display)
  1098. {
  1099.   cellptr = cell [col] [row];
  1100.   if (cellptr == NULL) return;
  1101.   switch (cellptr->attrib)
  1102.   {
  1103.     case TEXTCELL:
  1104.       clearoflags (col + 1, row, display);
  1105.     break;
  1106.    
  1107.     case VALUE: break;
  1108.     case FORMULA: break;
  1109.   }
  1110.   format [col] [row] &= ~OVERWRITE;
  1111.   GlobalFree (cell [col] [row]);
  1112.   cell [col] [row] = NULL;
  1113.   if (col == lastcol) setlastcol ();
  1114.   if (row == lastrow) setlastrow ();
  1115.   updateoflags (col, row, display);
  1116.   changed = TRUE;
  1117. }
  1118.  
  1119. //allocates space for a formula cell
  1120. INT allocformula (INT col, INT row, LPSTR s, DOUBLE amt)
  1121. {
  1122.   cellptr = (CELLPTR) GlobalAlloc 
  1123.     (GMEM_FIXED, sizeof (CELLREC));
  1124.   if (cellptr == NULL) return (FALSE);
  1125.   cellptr->attrib = FORMULA;
  1126.   strcpy (cellptr->v.f.formula, s);
  1127.   cellptr->v.f.fvalue = amt;
  1128.   cell [col] [row] = cellptr;
  1129.   return (TRUE);
  1130. }
  1131.  
  1132. //allocates space for a value cell
  1133. INT allocvalue (INT col, INT row, DOUBLE amt)
  1134. {
  1135.   cellptr = (CELLPTR) GlobalAlloc 
  1136.     (GMEM_FIXED, sizeof (CELLREC));
  1137.   if (cellptr == NULL) return (FALSE);
  1138.   cellptr->attrib = VALUE;
  1139.   cellptr->v.value = amt;
  1140.   cell [col] [row] = cellptr;
  1141.   return (TRUE);
  1142. }
  1143.  
  1144. //allocates space for a text cell
  1145. INT alloctext (INT col, INT row, LPSTR s)
  1146. {
  1147.   cellptr = (CELLPTR) GlobalAlloc 
  1148.     (GMEM_FIXED, sizeof (CELLREC));
  1149.   if (cellptr == NULL) return (FALSE);
  1150.   cellptr->attrib = TEXTCELL;
  1151.   strcpy (cellptr->v.text, s);
  1152.   cell [col] [row] = cellptr;
  1153.   return (TRUE);
  1154. }
  1155.  
  1156. //completes a reduction
  1157. VOID reduce (INT reduction)
  1158. {
  1159.   switch (reduction)
  1160.   {
  1161.     case RPLUS:
  1162.       token1 = stack [stacktop--];
  1163.       stack [stacktop--];
  1164.       token2 = stack [stacktop--];
  1165.       curtoken.x.value = token1.x.value + token2.x.value;
  1166.     break;
  1167.     
  1168.     case RMINUS:
  1169.       token1 = stack [stacktop--];
  1170.       stack [stacktop--];
  1171.       token2 = stack [stacktop--];
  1172.       curtoken.x.value = token2.x.value - token1.x.value;
  1173.     break;
  1174.     
  1175.     case RTIMES:
  1176.       token1 = stack [stacktop--];
  1177.       stack [stacktop--];
  1178.       token2 = stack [stacktop--];
  1179.       curtoken.x.value = token1.x.value * token2.x.value;
  1180.     break;
  1181.     
  1182.     case RDIVIDE:
  1183.       token1 = stack [stacktop--];
  1184.       stack [stacktop--];
  1185.       token2 = stack [stacktop--];
  1186.       if (token1.x.value == 0)
  1187.         curtoken.x.value = HUGE_VAL;
  1188.       else
  1189.         curtoken.x.value = token2.x.value / 
  1190.           token1.x.value;
  1191.     break;
  1192.     
  1193.     case RPOW:
  1194.       token1 = stack [stacktop--];
  1195.       stack [stacktop--];
  1196.       token2 = stack [stacktop--];
  1197.       curtoken.x.value = pow (token2.x.value, 
  1198.         token1.x.value);
  1199.     break;
  1200.     
  1201.     case RNEGATE:
  1202.       token1 = stack [stacktop--];
  1203.       stack [stacktop--];
  1204.       curtoken.x.value = -token1.x.value;
  1205.     break;
  1206.     
  1207.     case RSUM:
  1208.       token1 = stack [stacktop--];
  1209.       stack [stacktop--];
  1210.       token2 = stack [stacktop--];
  1211.       curtoken.x.value = 0;
  1212.       if (token1.x.c.row == token2.x.c.row)
  1213.       {
  1214.         if (token1.x.c.col < token2.x.c.col)
  1215.           error = TRUE;
  1216.         else
  1217.           for (counter = token2.x.c.col; 
  1218.             counter <= token1.x.c.col; counter++)
  1219.             curtoken.x.value += cellvalue (counter, 
  1220.               token1.x.c.row);
  1221.       }
  1222.       else if (token1.x.c.col == token2.x.c.col)
  1223.       {
  1224.         if (token1.x.c.row < token2.x.c.row)
  1225.           error = TRUE;
  1226.         else
  1227.           for (counter = token2.x.c.row; 
  1228.             counter <= token1.x.c.row; counter++)
  1229.               curtoken.x.value += 
  1230.                 cellvalue (token1.x.c.col, counter);
  1231.       }
  1232.       else
  1233.         error = TRUE;
  1234.     break;
  1235.     
  1236.     case RCELLVALUE:
  1237.       curtoken = stack [stacktop--];
  1238.       curtoken.x.value = cellvalue (curtoken.x.c.col, 
  1239.         curtoken.x.c.row);
  1240.     break;
  1241.     
  1242.     case RCPAREN:
  1243.       stack [stacktop--];
  1244.       curtoken = stack [stacktop--];
  1245.       stack [stacktop--];
  1246.     break;
  1247.     
  1248.     case RFUNC:
  1249.       stack [stacktop--];
  1250.       curtoken = stack [stacktop--];
  1251.       stack [stacktop--];
  1252.       token1 = stack [stacktop--];
  1253.       if (strcmp (token1.x.funcname, "ABS") == 0)
  1254.         curtoken.x.value = fabs (curtoken.x.value);
  1255.       else if (strcmp (token1.x.funcname, "ACOS") == 0)
  1256.         curtoken.x.value = acos (curtoken.x.value);
  1257.       else if (strcmp (token1.x.funcname, "ASIN") == 0)
  1258.         curtoken.x.value = asin (curtoken.x.value);
  1259.       else if (strcmp (token1.x.funcname, "ATAN") == 0)
  1260.         curtoken.x.value = atan (curtoken.x.value);
  1261.       else if (strcmp (token1.x.funcname, "COSH") == 0)
  1262.         curtoken.x.value = cosh (curtoken.x.value);
  1263.       else if (strcmp (token1.x.funcname, "COS") == 0)
  1264.         curtoken.x.value = cos (curtoken.x.value);
  1265.       else if (strcmp (token1.x.funcname, "EXP") == 0)
  1266.         curtoken.x.value = exp (curtoken.x.value);
  1267.       else if (strcmp (token1.x.funcname, "LOG10") == 0)
  1268.         curtoken.x.value = log10 (curtoken.x.value);
  1269.       else if (strcmp (token1.x.funcname, "LOG") == 0)
  1270.         curtoken.x.value = log (curtoken.x.value);
  1271.       else if (strcmp (token1.x.funcname, "ROUND") == 0)
  1272.         curtoken.x.value = (INT) (curtoken.x.value + 0.5);
  1273.       else if (strcmp (token1.x.funcname, "POW10") == 0)
  1274.         curtoken.x.value = pow (10, curtoken.x.value);
  1275.       else if (strcmp (token1.x.funcname, "SINH") == 0)
  1276.         curtoken.x.value = sinh (curtoken.x.value);
  1277.       else if (strcmp (token1.x.funcname, "SIN") == 0)
  1278.         curtoken.x.value = sin (curtoken.x.value);
  1279.       else if (strcmp (token1.x.funcname, "SQRT") == 0)
  1280.         curtoken.x.value = sqrt (curtoken.x.value);
  1281.       else if (strcmp (token1.x.funcname, "SQR") == 0)
  1282.         curtoken.x.value *= curtoken.x.value;
  1283.       else if (strcmp (token1.x.funcname, "TANH") == 0)
  1284.         curtoken.x.value = tanh (curtoken.x.value);
  1285.       else if (strcmp (token1.x.funcname, "TAN") == 0)
  1286.         curtoken.x.value = tan (curtoken.x.value);
  1287.       else if (strcmp (token1.x.funcname, "TRUNC") == 0)
  1288.         curtoken.x.value = (INT) curtoken.x.value;
  1289.     break;
  1290.     
  1291.     case RFIRSTTIMES: case RFIRSTPOW: case RFIRSTNEGATE: 
  1292.     case RFIRSTSUM: case RFIRSTFUNC: case RNUM: 
  1293.       curtoken = stack [stacktop--]; 
  1294.     break;
  1295.   }
  1296.  
  1297.   if (reduction <= 3) //RPLUS, RMINUS, RFIRSTTIMES
  1298.     switch (stack [stacktop].state)
  1299.     {
  1300.       case SFIRST: curtoken.state = SFIRSTPLUS; break;
  1301.       case SOPAREN: curtoken.state = SOPARENPLUS; break;
  1302.       case SFOPAREN: curtoken.state = SFOPARENPLUS; break;
  1303.     }
  1304.   else if (reduction <= 6) //RTIMES, RDIVIDE, RFIRSTPOW
  1305.     switch (stack [stacktop].state)
  1306.     {
  1307.       case SFIRST: case SOPAREN: case SFOPAREN: 
  1308.         curtoken.state = SFIRSTTIMES;
  1309.       break;
  1310.       
  1311.       case SPLUS: curtoken.state = SPLUSTIMES; break;
  1312.       case SMINUS: curtoken.state = SMINUSTIMES; break;
  1313.     }
  1314.   else if (reduction <= 8) //RPOW, RFIRSTNEGATE
  1315.     switch (stack [stacktop].state)
  1316.     {
  1317.       case SFIRST: case SOPAREN: case SPLUS: case SMINUS: 
  1318.       case SFOPAREN: 
  1319.         curtoken.state = SFIRSTPOW;
  1320.       break;
  1321.       
  1322.       case STIMES: curtoken.state = SDOTIMES; break;
  1323.       case SDIVIDE: curtoken.state = SDODIVIDE; break;
  1324.       case SPOW: curtoken.state = SDOPOW; break;
  1325.     }
  1326.   else if (reduction <= 10) //RNEGATE, RFIRSTSUM
  1327.     switch (stack [stacktop].state)
  1328.     {
  1329.       case SFIRST: case SOPAREN: case SPLUS: case SMINUS: 
  1330.       case STIMES: case SDIVIDE: case SPOW: 
  1331.       case SFOPAREN: 
  1332.         curtoken.state = SFIRSTNEGATE;
  1333.       break;
  1334.     }
  1335.   else if (reduction <= 12) //RSUM, RFIRSTFUNC
  1336.     switch (stack [stacktop].state)
  1337.     {
  1338.       case SFIRST: case SOPAREN: case SPLUS: case SMINUS: 
  1339.       case STIMES: case SDIVIDE: case SPOW: 
  1340.       case SFOPAREN:
  1341.         curtoken.state = SFIRSTSUM;
  1342.       break;
  1343.       
  1344.       case SNEGATE: curtoken.state = SDONEGATE;
  1345.     }
  1346.   else //RCELLVALUE, RCPAREN, RNUM, RFUNC
  1347.     switch (stack [stacktop].state)
  1348.     {
  1349.       case SFIRST: case SNEGATE: case SOPAREN: case SPLUS: 
  1350.       case SMINUS: case STIMES: case SDIVIDE: 
  1351.       case SPOW: case SFOPAREN: 
  1352.         curtoken.state = SFIRSTFUNC;
  1353.       break;
  1354.       
  1355.       default: curtoken.state = SERROR; break;
  1356.     }
  1357.    
  1358.   if (stacktop == PARSERSTACKSIZE - 1)
  1359.   {
  1360.     errormsg (MSGSTACKERROR);
  1361.     error = TRUE;
  1362.   }
  1363.   else
  1364.     stack [++stacktop] = curtoken;
  1365. }
  1366.  
  1367. //shifts token onto the stack
  1368. VOID shift (INT state)
  1369. {
  1370.   curtoken.state = state;
  1371.   if (stacktop == PARSERSTACKSIZE - 1)
  1372.   {
  1373.     errormsg (MSGSTACKERROR);
  1374.     error = TRUE;
  1375.   }
  1376.   else
  1377.     stack [++stacktop] = curtoken;
  1378.  
  1379.   while (*input == ' ') input++;
  1380.   if (*input == 0)
  1381.   {
  1382.     tokentype = EOLN;
  1383.     return;
  1384.   }
  1385.   if (strchr ("0123456789.", *input))
  1386.   {
  1387.     start = input;
  1388.     len = 0;
  1389.     decimal = FALSE;
  1390.     while ((*input >= '0' &&
  1391.       *input <= '9') || (*input == '.' && 
  1392.       !decimal))
  1393.     {
  1394.       if (*input == '.') decimal = TRUE;
  1395.       input++;
  1396.       len++;
  1397.     }
  1398.     if (len == 1 && start [0] == '.')
  1399.     {
  1400.       tokentype = BAD;
  1401.       return;
  1402.     }
  1403.     if (*input == 'E')
  1404.     {
  1405.       input++;
  1406.       len++;
  1407.       if (strchr ("+-", *input) != NULL)
  1408.       {
  1409.         input++;
  1410.         len++;
  1411.       }
  1412.       numlen = 0;
  1413.       while (*input >= '0' && 
  1414.         *input <= '9' && ++numlen <= 3)
  1415.       {
  1416.         input++;
  1417.         len++;
  1418.       }
  1419.     }
  1420.     strncpy (numstring, start, len);
  1421.     numstring [len] = 0;
  1422.     curtoken.x.value = atof (numstring);
  1423.     if (*_errno () == 34)
  1424.     {
  1425.       tokentype = BAD;
  1426.       return;
  1427.     }
  1428.     tokentype = NUM;
  1429.     return;
  1430.   }
  1431.   else if (IsCharAlpha (*input))
  1432.   {
  1433.     if (isfunc ("ABS") || isfunc ("ACOS") || 
  1434.       isfunc ("ASIN") || isfunc ("ATAN") || 
  1435.       isfunc ("COSH") || isfunc ("COS") ||
  1436.       isfunc ("EXP") || isfunc ("LOG10") || 
  1437.       isfunc ("LOG") || isfunc ("POW10") || 
  1438.       isfunc ("ROUND") || isfunc ("SINH") ||
  1439.       isfunc ("SIN") || isfunc ("SQRT") || 
  1440.       isfunc ("SQR") || isfunc ("TANH") || 
  1441.       isfunc ("TAN") || isfunc ("TRUNC"))
  1442.     { 
  1443.       tokentype = FUNC;
  1444.       return;
  1445.     }
  1446.     if (formulastart (&input, &curtoken.x.c.col, 
  1447.       &curtoken.x.c.row))
  1448.     {
  1449.       isformula = TRUE;
  1450.       tokentype = CELL;
  1451.       return;
  1452.     }
  1453.     else
  1454.     {
  1455.       tokentype = BAD;
  1456.       return;
  1457.     }
  1458.   }
  1459.   else 
  1460.     switch (*(input++))
  1461.     {
  1462.       case '+': tokentype = PLUS; return;
  1463.       case '-': tokentype = MINUS; return;
  1464.       case '*': tokentype = TIMES; return;
  1465.       case '/': tokentype = DIVIDE; return;
  1466.       case '^': tokentype = EXP; return;
  1467.       case ':': tokentype = COLON; return;
  1468.       case '(': tokentype = OPAREN; return;
  1469.       case ')': tokentype = CPAREN; return;
  1470.       default: tokentype = BAD; return;
  1471.   }
  1472. }
  1473.  
  1474. //parses string s, returns value of the evaluated string,
  1475. //puts in att: TEXTCELL = 0, CONSTANT = 1, FORMULA = 2
  1476. DOUBLE parse (LPSTR s, LPINT att)
  1477. {
  1478.   accepted = FALSE;
  1479.   error = FALSE;
  1480.   isformula = FALSE;
  1481.   input = copy;
  1482.   strupr (strcpy (copy, s));
  1483.   stacktop = -1;
  1484.   curtoken.x.value = 0;
  1485.   shift (SFIRST);
  1486.   do
  1487.   {
  1488.     switch (stack [stacktop].state)
  1489.     {
  1490.       case SFIRST: case SOPAREN: case SPLUS: case SMINUS: 
  1491.       case STIMES: case SDIVIDE: case SPOW: 
  1492.       case SFOPAREN:
  1493.         if (tokentype == NUM)
  1494.           shift (SNUM);
  1495.         else if (tokentype == CELL)
  1496.           shift (SCELL);
  1497.         else if (tokentype == FUNC)
  1498.           shift (SFUNC);
  1499.         else if (tokentype == MINUS)
  1500.           shift (SNEGATE);
  1501.         else if (tokentype == OPAREN)
  1502.           shift (SOPAREN);
  1503.         else
  1504.           error = TRUE;
  1505.       break;
  1506.       
  1507.       case SFIRSTPLUS:
  1508.         if (tokentype == EOLN)
  1509.           accepted = TRUE;
  1510.         else if (tokentype == PLUS)
  1511.           shift (SPLUS);
  1512.         else if (tokentype == MINUS)
  1513.           shift (SMINUS);
  1514.         else
  1515.           error = TRUE;
  1516.       break;
  1517.       
  1518.       case SFIRSTTIMES:
  1519.         if (tokentype == TIMES)
  1520.           shift (STIMES);
  1521.         else if (tokentype == DIVIDE)
  1522.           shift (SDIVIDE);
  1523.         else
  1524.           reduce (RFIRSTTIMES);
  1525.       break;
  1526.       
  1527.       case SFIRSTPOW: reduce (RFIRSTPOW); break;
  1528.       
  1529.       case SFIRSTNEGATE:
  1530.         if (tokentype == EXP)
  1531.           shift (SPOW);
  1532.         else
  1533.           reduce (RFIRSTNEGATE);
  1534.       break;
  1535.       
  1536.       case SNEGATE:
  1537.         if (tokentype == NUM)
  1538.           shift (SNUM);
  1539.         else if (tokentype == CELL)
  1540.           shift (SCELL);
  1541.         else if (tokentype == FUNC)
  1542.           shift (SFUNC);
  1543.         else if (tokentype == OPAREN)
  1544.           shift (SOPAREN);
  1545.         else
  1546.           error = TRUE;
  1547.       break;
  1548.       
  1549.       case SFIRSTSUM: reduce (RFIRSTSUM); break;
  1550.       
  1551.       case SCELL:
  1552.         if (tokentype == COLON)
  1553.           shift (SCOLON);
  1554.         else
  1555.           reduce (RCELLVALUE);
  1556.       break;
  1557.       
  1558.       case SFIRSTFUNC: reduce (RFIRSTFUNC); break;
  1559.       case SNUM: reduce (RNUM); break;
  1560.       
  1561.       case SFUNC:
  1562.         if (tokentype == OPAREN)
  1563.           shift (SFOPAREN);
  1564.         else
  1565.           error = TRUE;
  1566.       break;
  1567.       
  1568.       case SDONEGATE: reduce (RNEGATE); break;
  1569.       
  1570.       case SCOLON:
  1571.         if (tokentype == CELL)
  1572.           shift (SDOSUM);
  1573.         else
  1574.           error = TRUE;
  1575.       break;
  1576.       
  1577.       case SOPARENPLUS:
  1578.         if (tokentype == PLUS)
  1579.           shift (SPLUS);
  1580.         else if (tokentype == MINUS)
  1581.           shift (SMINUS);
  1582.         else if (tokentype == CPAREN)
  1583.           shift (SCPAREN);
  1584.         else
  1585.           error = TRUE;
  1586.       break;
  1587.       
  1588.       case SPLUSTIMES:
  1589.         if (tokentype == TIMES)
  1590.           shift (STIMES);
  1591.         else if (tokentype == DIVIDE)
  1592.           shift (SDIVIDE);
  1593.         else
  1594.           reduce (RPLUS);
  1595.       break;
  1596.       
  1597.       case SMINUSTIMES:
  1598.         if (tokentype == TIMES)
  1599.           shift (STIMES);
  1600.         else  if (tokentype == DIVIDE)
  1601.           shift (SDIVIDE);
  1602.         else
  1603.           reduce (RMINUS);
  1604.       break;
  1605.       
  1606.       case SDOTIMES: reduce (RTIMES); break;
  1607.       case SDODIVIDE: reduce (RDIVIDE); break;
  1608.       case SDOPOW: reduce (RPOW); break;
  1609.       case SDOSUM: reduce (RSUM); break;
  1610.       case SCPAREN: reduce (RCPAREN); break;
  1611.       
  1612.       case SFOPARENPLUS:
  1613.         if (tokentype == PLUS)
  1614.           shift (SPLUS);
  1615.         else if (tokentype == MINUS)
  1616.           shift (SMINUS);
  1617.         else if (tokentype == CPAREN)
  1618.           shift (SDOFUNC);
  1619.         else 
  1620.           error = TRUE;
  1621.       break;
  1622.       
  1623.       case SDOFUNC: reduce (RFUNC); break;
  1624.       case SERROR: error = TRUE; break;
  1625.     }
  1626.   }
  1627.   while (!accepted && !error);
  1628.   if (error)
  1629.   {
  1630.     *att = TEXTCELL;
  1631.     return (0);
  1632.   }
  1633.   if (isformula)
  1634.     *att = FORMULA;
  1635.   else
  1636.     *att = VALUE;
  1637.   strcpy (s, copy);
  1638.   return (stack [stacktop].x.value);
  1639. }
  1640.  
  1641. //finds the value of a particular cell
  1642. DOUBLE cellvalue (INT col, INT row)
  1643. {
  1644.   if (cell [col] [row] == NULL) return (0);
  1645.   if (cell [col] [row]->attrib == TEXTCELL) 
  1646.     return (HUGE_VAL);
  1647.   if (cell [col] [row]->attrib == FORMULA)
  1648.     return (cell [col] [row]->v.f.fvalue);
  1649.   return (cell [col] [row]->v.value);
  1650. }
  1651.  
  1652. //returns TRUE if the string is a legal function 
  1653. INT isfunc (LPSTR s)
  1654. {
  1655.   len = lstrlen (s);
  1656.   if (strncmp (s, input, len) == 0)
  1657.   {
  1658.     strncpy (curtoken.x.funcname, input, len);
  1659.     curtoken.x.funcname [len] = 0;
  1660.     input += len;
  1661.     return (TRUE);
  1662.   }
  1663.   return (FALSE);
  1664. }
  1665.  
  1666. //gets yes or no answer, returns TRUE if ESC was pressed
  1667. INT getyesno (LPINT yesno, LPSTR prompt)
  1668. {
  1669.   writef (1, 24, PROMPTCOLOR, 80, prompt);
  1670.   setcursor (shortcursor);
  1671.   do
  1672.   {
  1673.     *yesno = (CHAR) CharUpper ((LPSTR) getkey ());
  1674.     if (*yesno == ESC)
  1675.     {
  1676.       setcursor (nocursor);
  1677.       return (FALSE);
  1678.     }
  1679.   } 
  1680.   while (strchr ("YN", *yesno) == NULL);
  1681.   setcursor (nocursor);
  1682.   return (TRUE);
  1683. }
  1684.  
  1685. //reads cell name, returns FALSE if ESC was pressed
  1686. INT getcell (LPINT col, LPINT row)
  1687. {
  1688.   first = TRUE;
  1689.   good = FALSE;
  1690.   oldcol = *col;
  1691.   oldrow = *row;
  1692.   numlen = rowwidth (MAXROWS);
  1693.   data [0] = 0;
  1694.   do
  1695.   {
  1696.     if (!first) errormsg (MSGBADCELL);
  1697.     first = FALSE;
  1698.     input = data;
  1699.     if (!editstring (data, "", numlen + 2))
  1700.     {
  1701.       *col = oldcol;
  1702.       *row = oldrow;
  1703.       return (FALSE);
  1704.     }
  1705.     *col = (CHAR) CharUpper ((LPSTR) *(input++)) - 'A';
  1706.     if (IsCharAlpha (*input))
  1707.     {
  1708.       *col *= 26;
  1709.       *col += (CHAR) CharUpper ((LPSTR) *(input++)) - 
  1710.         'A' + 26;
  1711.     }
  1712.     if (*col >= MAXCOLS) continue;
  1713.     start = input;
  1714.     for (len = 0; len < numlen; len++)
  1715.       if (*(input++) < '0' || *(input++) > '9')
  1716.       {
  1717.         input--;
  1718.         break;
  1719.       }
  1720.     if (len == 0) continue;
  1721.     strncpy (numstring, start, len);
  1722.     numstring [len] = 0;
  1723.     *row = atoi (numstring) - 1;
  1724.     if ((*row >= MAXROWS) || (*row == -1) || 
  1725.       (*input != 0)) continue;
  1726.     good = TRUE;
  1727.   } 
  1728.   while (!good);
  1729.   return (TRUE);
  1730. }
  1731.  
  1732. //reads in a positive integer from low to high
  1733. INT getint (LPINT number, INT low, INT high)
  1734. {
  1735.   good = FALSE;
  1736.   str [0] = 0;
  1737.   sprintf (cstr, MSGBADNUMBER, low, high);
  1738.   do
  1739.   {
  1740.     if (!editstring (str, "1234567890", 4)) 
  1741.       return (FALSE);
  1742.     i = atoi (str);
  1743.     if ((good = (i >= low && i <= high)) == 0) 
  1744.       errormsg (cstr);
  1745.   } 
  1746.   while (!good);
  1747.   *number = i;
  1748.   return (TRUE);
  1749. }
  1750.  
  1751. VOID setcursor (UINT shape)
  1752. {
  1753.   if (shape == nocursor)
  1754.     curinfo.bVisible = FALSE;
  1755.   else
  1756.     curinfo.bVisible = TRUE;
  1757.   if (shape == shortcursor) curinfo.dwSize = 20;    
  1758.   if (shape == tallcursor) curinfo.dwSize = 90;
  1759.   SetConsoleCursorInfo (so, &curinfo);   
  1760. }
  1761.  
  1762. //allows the user to edit a string, returns TRUE if ESC
  1763. //was not pressed, FALSE otherwise
  1764. INT editstring (LPSTR s, LPSTR legal, INT maxlength)
  1765. {
  1766.   len = lstrlen (s);
  1767.   pos = len;
  1768.   insert = TRUE;
  1769.   setcursor (shortcursor);
  1770.   do
  1771.   {
  1772.     writef (1, 25, TEXTCOLOR, 79, "%s", s);
  1773.     SetConsoleCursorPosition (so, (COORD) {pos, 24});
  1774.     switch (c = getkey())
  1775.     {
  1776.       case HOMEKEY: pos = 0; break;
  1777.       case ENDKEY: pos = len; break;
  1778.       
  1779.       case INSKEY:
  1780.         insert = !insert;
  1781.         if (insert)
  1782.           setcursor (shortcursor);
  1783.         else
  1784.           setcursor (tallcursor);
  1785.       break;
  1786.       
  1787.       case LEFTKEY: if (pos > 0) pos--; break;
  1788.       case RIGHTKEY: if (pos < len) pos++; break;
  1789.       
  1790.       case BS:
  1791.         if (pos > 0)
  1792.         {
  1793.           MoveMemory (&s [pos - 1], &s [pos], 
  1794.             len - pos + 1);
  1795.           pos--;
  1796.           len--;
  1797.         }
  1798.       break;
  1799.       
  1800.       case DELKEY:
  1801.         if (pos < len)
  1802.         {
  1803.           MoveMemory (&s [pos], &s [pos + 1], len - pos);
  1804.           len--;
  1805.         }
  1806.       break;
  1807.       
  1808.       case CR: case TABKEY: case F4: case CTRLLEFTKEY:
  1809.       case CTRLRIGHTKEY: case PGUPKEY: case PGDNKEY:
  1810.       case CTRLC: case CTRLV: case ALTF4: case F2:
  1811.       case SHIFTUPKEY: case SHIFTDOWNKEY: case SHIFTF9:
  1812.       case SHIFTLEFTKEY: case SHIFTRIGHTKEY: case F9:
  1813.       break;
  1814.       
  1815.       case UPKEY: c = CR; break;
  1816.       case DOWNKEY: c = CR; break;
  1817.       case ESC: len = 0; break;
  1818.       
  1819.       default:
  1820.         if ((legal [0] == 0 || strchr (legal, c) != NULL) &&
  1821.           len < maxlength)
  1822.         {
  1823.           if (insert)
  1824.           {
  1825.             memmove (&s [pos + 1], &s [pos], len - 
  1826.               pos + 1);
  1827.             len++;
  1828.           }
  1829.           else
  1830.             if (pos >= len) len++;
  1831.           s [pos++] = c;
  1832.         }
  1833.       break;
  1834.     }
  1835.     s [len] = 0;
  1836.   } 
  1837.   while (c != CR && c != ESC);
  1838.   scroll (UP, 0, 1, 25, 80, 25, TEXTCOLOR);
  1839.   SetConsoleCursorPosition (so, (COORD) {0, 24});
  1840.   setcursor (shortcursor);
  1841.   setcursor (nocursor);
  1842.   return (c != ESC);
  1843. }
  1844.  
  1845. //displays the type of cell and what is in it
  1846. VOID showcelltype (VOID)
  1847. {
  1848.   formdisplay = !formdisplay;
  1849.   s = cellstring (curcol, currow, &color, NOFORMAT);
  1850.   colstring (curcol, colstr);
  1851.   if (curcell == NULL)
  1852.     writef (1, 23, CELLTYPECOLOR, 80, "%s%d %s", colstr, 
  1853.       currow + 1, MSGEMPTY);
  1854.   else
  1855.     switch (curcell->attrib)
  1856.     {
  1857.       case TEXTCELL:
  1858.         writef (1, 23, CELLTYPECOLOR, 80, "%s%d %s", 
  1859.           colstr, currow + 1, MSGTEXT);
  1860.       break;
  1861.       
  1862.       case VALUE:
  1863.         writef (1, 23, CELLTYPECOLOR, 80, "%s%d %s", 
  1864.           colstr, currow + 1, MSGVALUE);
  1865.       break;
  1866.       
  1867.       case FORMULA:
  1868.         writef (1, 23, CELLTYPECOLOR, 80, "%s%d %s", 
  1869.           colstr, currow + 1, MSGFORMULA);
  1870.       break;
  1871.     }
  1872.   writef (1, 24, CELLCONTENTSCOLOR, 80, "%s", s);
  1873.   formdisplay = !formdisplay;
  1874. }
  1875.  
  1876. VOID displayscreen (INT updating)
  1877. {
  1878.   for (row = toprow; row <= bottomrow; row++)
  1879.     displayrow (row, updating);
  1880.   clearlastcol ();
  1881. }
  1882.  
  1883. VOID displayrow (INT row, INT updating)
  1884. {
  1885.   for (col = leftcol; col <= rightcol; col++)
  1886.     displaycell (col, row, NOHIGHLIGHT, updating);
  1887. }
  1888.  
  1889. VOID displaycol (INT col, INT updating)
  1890. {
  1891.   for (row = toprow; row <= bottomrow; row++)
  1892.     displaycell (col, row, NOHIGHLIGHT, updating);
  1893. }
  1894.  
  1895. VOID displaycell (INT col, INT row, INT highlighting, 
  1896.   INT updating)
  1897. {
  1898.   if (loading == 1) return;
  1899.   if ((updating) &&
  1900.     ((cell [col] [row] == NULL) || 
  1901.     (cell [col] [row]->attrib != FORMULA)))
  1902.     return;
  1903.   s = cellstring (col, row, &color, FORMAT);
  1904.   if (highlighting || (col >= curcol && col <= curcolb &&
  1905.     row >= currow && row <= currowb &&
  1906.     (curcol != curcolb || currow != currowb)))
  1907.   {
  1908.     if (color == ERRORCOLOR)
  1909.       color = HIGHLIGHTERRORCOLOR;
  1910.     else
  1911.       color = HIGHLIGHTCOLOR;
  1912.   }
  1913.   writef (colstart [col - leftcol] + 1, 
  1914.     row - toprow + 3, color, colwidth [col], "%s", s);
  1915. }
  1916.  
  1917. VOID clearlastcol (VOID)
  1918. {
  1919.   if ((col = colstart [rightcol - leftcol] + 
  1920.     colwidth [rightcol]) < 80)
  1921.     scroll (UP, 0, col + 1, 3, 80, SCREENROWS + 2, 
  1922.       TEXTCOLOR);
  1923. }
  1924.  
  1925. //prints the column headings
  1926. VOID printcol (VOID)
  1927. {
  1928.  writef (1, 2, HEADERCOLOR, 3, "");
  1929.  scroll (UP, 0, 1, 2, 80, 2, HEADERCOLOR);
  1930.  for (col = leftcol; col <= rightcol; col++)
  1931.  {
  1932.   centercolstring (col, colstr);
  1933.   writef (colstart [col - leftcol] + 1, 2, HEADERCOLOR, 
  1934.     colwidth [col], colstr);
  1935.  }
  1936. }
  1937.  
  1938. VOID insertrow (INT row)
  1939. {
  1940.   if (lastrow == MAXROWS - 1)
  1941.   {
  1942.     for (counter = 0; counter <= lastcol; counter++)
  1943.       deletecell (counter, lastrow, NOUPDATE);
  1944.     printfreemem ();
  1945.   }
  1946.   if (row != MAXROWS - 1)
  1947.     for (counter = 0; counter < MAXCOLS; counter++)
  1948.     {
  1949.       MoveMemory (&cell [counter] [row + 1],
  1950.         &cell [counter] [row], 
  1951.         sizeof (CELLPTR) * (MAXROWS - row - 1));
  1952.       MoveMemory (&format [counter] [row + 1],
  1953.         &format [counter] [row], MAXROWS - row - 1);
  1954.     }
  1955.   for (counter = 0; counter < MAXCOLS; counter++)
  1956.   {
  1957.     cell [counter] [row] = NULL;
  1958.     format [counter] [row] = DEFAULTFORMAT;
  1959.   }
  1960.   lastrow = MAXROWS - 1;
  1961.   setlastrow ();
  1962.   for (counter = 0; counter <= lastcol; counter++)
  1963.     for (rowc = 0; rowc <= lastrow; rowc++)
  1964.       if (cell [counter] [rowc] != NULL &&
  1965.         cell [counter] [rowc]->attrib == FORMULA)
  1966.         fixformula (counter, rowc, ROWADD, row);
  1967.   while (row <= bottomrow) displayrow (row++, NOUPDATE);
  1968.   changed = TRUE;
  1969.   recalc ();
  1970. }
  1971.  
  1972. VOID deleterow (INT row)
  1973. {
  1974.   for (counter = 0; counter <= lastcol; counter++)
  1975.     deletecell (counter, row, NOUPDATE);
  1976.   printfreemem ();
  1977.   if (row != MAXROWS - 1)
  1978.     for (counter = 0; counter < MAXCOLS; counter++)
  1979.     {
  1980.       MoveMemory (&cell [counter] [row],
  1981.         &cell [counter] [row + 1], 
  1982.         sizeof (CELLPTR) * (MAXROWS - row - 1));
  1983.       MoveMemory (&format [counter] [row],
  1984.         &format [counter] [row + 1], MAXROWS - row - 1);
  1985.     }
  1986.   else
  1987.     for (counter = 0; counter <= lastcol; counter++)
  1988.     {
  1989.       cell [counter] [MAXROWS - 1] = NULL;
  1990.       format [counter] [MAXROWS - 1] = DEFAULTFORMAT;
  1991.     }
  1992.   if (lastrow >= row && lastrow > 0) lastrow--;
  1993.   for (counter = 0; counter <= lastcol; counter++)
  1994.     for (rowc = 0; rowc <= lastrow; rowc++)
  1995.       if (cell [counter] [rowc] != NULL &&
  1996.         cell [counter] [rowc]->attrib == FORMULA)
  1997.         fixformula (counter, rowc, ROWDEL, row);
  1998.   while (row <= bottomrow) displayrow (row++, NOUPDATE);
  1999.   changed = TRUE;
  2000.   recalc ();
  2001. }
  2002.  
  2003. VOID insertcol (INT col)
  2004. {
  2005.   if (lastcol == MAXCOLS - 1)
  2006.   {
  2007.     for (counter = 0; counter <= lastrow; counter++)
  2008.       deletecell (lastcol, counter, NOUPDATE);
  2009.     printfreemem ();
  2010.   }
  2011.   if (col != MAXCOLS - 1)
  2012.   {
  2013.     MoveMemory (&cell [col + 1] [0], &cell [col] [0], 
  2014.       MAXROWS * sizeof (CELLPTR) * (MAXCOLS - col - 1));
  2015.     MoveMemory (&format [col + 1] [0], &format [col] [0], 
  2016.       MAXROWS * (MAXCOLS - col - 1));
  2017.     MoveMemory (&colwidth [col + 1], &colwidth [col], 
  2018.       MAXCOLS - col - 1);
  2019.   }
  2020.   FillMemory (&cell [col] [0], 
  2021.     MAXROWS * sizeof (CELLPTR), 0);
  2022.   FillMemory (&format [col] [0], MAXROWS, DEFAULTFORMAT);
  2023.   colwidth [col] = DEFAULTWIDTH;
  2024.   lastcol = MAXCOLS - 1;
  2025.   setlastcol ();
  2026.   setrightcol ();
  2027.   if (curcol > rightcol)
  2028.   {
  2029.     rightcol++;
  2030.     setleftcol ();
  2031.   }
  2032.   for (counter = 0; counter <= lastcol; counter++)
  2033.     for (row = 0; row <= lastrow; row++)
  2034.     {
  2035.       if (cell [counter] [row] != NULL &&
  2036.         cell [counter] [row]->attrib == FORMULA)
  2037.         fixformula (counter, row, COLADD, col);
  2038.       updateoflags (col, row, NOUPDATE);
  2039.     }
  2040.   while (col <= rightcol) displaycol (col++, NOUPDATE);
  2041.   changed = TRUE;
  2042.   recalc ();
  2043. }
  2044.  
  2045. VOID deletecol (INT col)
  2046. {
  2047.   for (counter = 0; counter <= lastrow; counter++)
  2048.     deletecell (col, counter, NOUPDATE);
  2049.   printfreemem ();
  2050.   if (col != MAXCOLS - 1)
  2051.   {
  2052.     MoveMemory (&cell [col] [0], &cell [col + 1] [0],  
  2053.       MAXROWS * sizeof (CELLPTR) * (MAXCOLS - col - 1));
  2054.     MoveMemory (&format [col] [0], &format [col + 1] [0], 
  2055.       MAXROWS * (MAXCOLS - col - 1));
  2056.     MoveMemory (&colwidth [col], &colwidth [col + 1], 
  2057.       MAXCOLS - col - 1);
  2058.   }
  2059.   FillMemory (&cell [MAXCOLS - 1] [0], 
  2060.     MAXROWS * sizeof (CELLPTR), 0);
  2061.   FillMemory (&format [MAXCOLS - 1] [0], MAXROWS, 
  2062.     DEFAULTFORMAT);
  2063.   colwidth [MAXCOLS - 1] = DEFAULTWIDTH;
  2064.   if ((lastcol >= col) && (lastcol > 0)) lastcol--;
  2065.   setrightcol ();
  2066.   if (curcol > rightcol)
  2067.   {
  2068.     rightcol++;
  2069.     setleftcol ();
  2070.   }
  2071.   clearlastcol ();
  2072.   for (counter = 0; counter <= lastcol; counter++)
  2073.     for (row = 0; row <= lastrow; row++)
  2074.     {
  2075.       if (cell [counter] [row] != NULL &&
  2076.         cell [counter] [row]->attrib == FORMULA)
  2077.         fixformula (counter, row, COLDEL, col);
  2078.       updateoflags (col, row, NOUPDATE);
  2079.     }
  2080.   while (col <= rightcol) displaycol (col++, NOUPDATE);
  2081.   changed = TRUE;
  2082.   recalc ();
  2083. }
  2084.  
  2085. VOID formatcells (VOID)
  2086. {
  2087.   newformat = 0;
  2088.   if (!getyesno (&temp, MSGRIGHTJUST)) return;
  2089.   newformat += (temp == 'Y') * RJUSTIFY;
  2090.   if (!getyesno (&temp, MSGDOLLAR)) return;
  2091.   newformat += (temp == 'Y') * DOLLAR;
  2092.   if (!getyesno (&temp, MSGCOMMAS)) return;
  2093.   newformat += (temp == 'Y') * COMMAS;
  2094.   if (newformat & DOLLAR)
  2095.     newformat += 2;
  2096.   else
  2097.   {
  2098.     writef (1, 24, PROMPTCOLOR, 80, MSGPLACES);
  2099.     if (!getint (&temp, 0, MAXPLACES)) return;
  2100.     newformat += temp;
  2101.   }
  2102.   for (col = curcol; col <= curcolb; col++)
  2103.     for (row = currow; row <= currowb; row++)
  2104.       format [col] [row] = (format [col] [row] & 
  2105.         OVERWRITE) | newformat;
  2106.   displayscreen (NOUPDATE);
  2107. }
  2108.  
  2109. VOID setcolwidth (INT col)
  2110. {
  2111.   writef (1, 24, PROMPTCOLOR, 80, MSGCOLWIDTH);
  2112.   if (!getint (&width, MINCOLWIDTH, MAXCOLWIDTH)) return;
  2113.   colwidth [col] = width;
  2114.   setrightcol ();
  2115.   if (rightcol < col)
  2116.   {
  2117.     rightcol = col;
  2118.     setleftcol ();
  2119.     setrightcol ();
  2120.   }
  2121.   for (row = 0; row <= lastrow; row++)
  2122.   {
  2123.     if (cell [col] [row] != NULL && 
  2124.       cell [col] [row]->attrib == TEXTCELL)
  2125.       clearoflags (col + 1, row, NOUPDATE);
  2126.     else
  2127.       clearoflags (col, row, NOUPDATE);
  2128.     updateoflags (col, row, NOUPDATE);
  2129.   }
  2130.   displayscreen (NOUPDATE);
  2131. }
  2132.  
  2133. INT pagerows (INT row, INT toppage, INT border)
  2134. {
  2135.   INT rows;
  2136.   rows = toppage ? 66 - TOPMARGIN : 66;
  2137.   if (border) rows--;
  2138.   if (row + rows - 1 > lastrow)
  2139.     return (lastrow - row + 1);
  2140.   else
  2141.     return (rows);
  2142. }
  2143.  
  2144. INT pagecols (INT col, INT border, INT columns)
  2145. {
  2146.   INT len = ((col == 0) && (border)) ? columns - 
  2147.     LEFTMARGIN : columns;
  2148.   INT firstcol = col;
  2149.   while ((len > 0) && (col <= lastcol)) 
  2150.     len -= colwidth [col++];
  2151.   if (len < 0) col--;
  2152.   return (col - firstcol);
  2153. }
  2154.  
  2155. VOID printsheet (VOID)
  2156. {
  2157.   CHAR filename [MAXINPUT + 1], s [133], 
  2158.     colstr [MAXCOLWIDTH + 1];
  2159.   FILE *file;
  2160.   INT columns, counter1, counter2, counter3, col = 0, 
  2161.     row, border, toppage, lcol, lrow, dummy, printed, 
  2162.     oldlastcol;
  2163.   filename [0] = 0;
  2164.   lstrcpy (filename, "~.prn");
  2165.   if ((file = fopen (filename, "wt")) == NULL)
  2166.   {
  2167.     errormsg (MSGNOOPEN);
  2168.     return;
  2169.   }
  2170.   oldlastcol = lastcol;
  2171.   for (counter1 = 0; counter1 <= lastrow; counter1++)
  2172.     for (counter2 = lastcol; counter2 < MAXCOLS; 
  2173.       counter2++)
  2174.       if (format [counter2] [counter1] >= OVERWRITE) 
  2175.         lastcol = counter2;
  2176.   if (!getyesno (&columns, MSGCOLUMNS)) return;
  2177.   columns = (columns == 'Y') ? 131 : 79;
  2178.   if (!getyesno (&border, MSGBORDER)) return;
  2179.   border = (border == 'Y');
  2180.   while (col <= lastcol)
  2181.   {
  2182.     row = 0;
  2183.     toppage = TRUE;
  2184.     lcol = pagecols (col, border, columns) + col;
  2185.     while (row <= lastrow)
  2186.     {
  2187.       lrow = pagerows (row, toppage, border) + row;
  2188.       printed = 0;
  2189.       if (toppage)
  2190.         for (counter1 = 0; counter1 < TOPMARGIN; 
  2191.           counter1++)
  2192.         {
  2193.           fprintf (file, "\n");
  2194.         }
  2195.       for (counter1 = row; counter1 < lrow; counter1++)
  2196.       {
  2197.         if (border && counter1 == row && toppage)
  2198.         {
  2199.           if (col == 0 && border)
  2200.             sprintf (s, "%*s", LEFTMARGIN, "");
  2201.           else
  2202.             s [0] = 0;
  2203.           for (counter3 = col; counter3 < lcol; 
  2204.             counter3++)
  2205.           {
  2206.             centercolstring (counter3, colstr);
  2207.             strcat (s, colstr);
  2208.           }
  2209.           OemToChar (s, s);
  2210.           fprintf (file, "%s\n", s);
  2211.           printed++;
  2212.         }
  2213.         if (col == 0 && border)
  2214.           sprintf (s, "%-*d", LEFTMARGIN, counter1 + 1);
  2215.         else
  2216.           s [0] = 0;
  2217.         for (counter2 = col; counter2 < lcol; counter2++)
  2218.           strcat (s, cellstring (counter2, counter1, 
  2219.             &dummy, FORMAT));
  2220.         OemToChar (s, s);
  2221.         fprintf (file, "%s\n", s);
  2222.         printed++;
  2223.       }
  2224.       row = lrow;
  2225.       toppage = FALSE;
  2226.     }
  2227.     col = lcol;
  2228.   }
  2229.   fclose (file);
  2230.   lastcol = oldlastcol;
  2231.   ShellExecute (NULL, "open", "wordpad", "~.prn", 
  2232.     "", SW_SHOWDEFAULT);
  2233. }
  2234.  
  2235. VOID addformula (VOID)
  2236. {
  2237.   cstr [1] = 0;
  2238.   for (ii = 0; cellptr->v.f.formula [ii] != 0; ii++)
  2239.   {
  2240.     jj = ii;
  2241.     fstr = cellptr->v.f.formula + jj;
  2242.     if (formulastart (&fstr, &cl, &rw) && 
  2243.       *(fstr++) == ':' &&
  2244.       formulastart (&fstr, &cl, &rw))
  2245.     {
  2246.       lstrcat (str, "SUM(");
  2247.       jj = ii + fstr - cellptr->v.f.formula - jj;
  2248.       for (n = ii; n < jj; n++)
  2249.       {
  2250.         cstr [0] = cellptr->v.f.formula [n];
  2251.         lstrcat (str, cstr);
  2252.       }
  2253.       lstrcat (str, ")");
  2254.       ii = jj - 1;
  2255.     }
  2256.     else
  2257.     {
  2258.       cstr [0] = cellptr->v.f.formula [ii];
  2259.       lstrcat (str, cstr);
  2260.     }
  2261.   }
  2262. }
  2263.  
  2264. UINT APIENTRY hookproc2 (HWND hdlg, UINT uimsg, 
  2265.   WPARAM wparam, LPARAM lparam)
  2266. {
  2267.   SetForegroundWindow (GetParent (hdlg));
  2268.   return 0;
  2269. }
  2270.  
  2271. INT copys (VOID)
  2272. {
  2273.   fsize = 0;
  2274.   for (row = currow; row <= currowb; row++)
  2275.   {
  2276.     for (col = curcol; col <= curcolb; col++)
  2277.     {
  2278.       if (cell [col] [row] != NULL)
  2279.       {
  2280.         cellptr = cell [col] [row];
  2281.         switch (cellptr->attrib)
  2282.         {
  2283.           case TEXTCELL:
  2284.             fsize = fsize + lstrlen (cellptr->v.text); 
  2285.           break;
  2286.  
  2287.           case VALUE:
  2288.             sprintf (str, "%1.*f", format [col] [row] & 
  2289.               15, cellptr->v.value);
  2290.             fsize = fsize + lstrlen (str);
  2291.           break;
  2292.           
  2293.           case FORMULA:
  2294.             if (formdisplay)
  2295.             {
  2296.               lstrcpy (str, "=");
  2297.               fixformula2 (col, row, -curcol, -currow);
  2298.               addformula ();
  2299.               fixformula2 (col, row, curcol, currow);
  2300.               fsize = fsize + lstrlen (str);
  2301.             }
  2302.             else
  2303.             {
  2304.               sprintf (str, "%1.*f", format [col] [row] &
  2305.                 15, cellptr->v.value);
  2306.               fsize = fsize + lstrlen (str);
  2307.             }
  2308.           break;
  2309.         }
  2310.       }
  2311.       if (col != lastcol) fsize++;
  2312.     }
  2313.     fsize = fsize + 2;
  2314.   }
  2315.   ctext = GlobalAlloc (GMEM_MOVEABLE | 
  2316.     GMEM_DDESHARE, fsize + 1);
  2317.   if (ctext == NULL) return (FALSE);
  2318.   ftext = (LPSTR) GlobalLock (ctext);
  2319.   ftext [0] = 0;      
  2320.   for (row = currow; row <= currowb; row++)
  2321.   {
  2322.     for (col = curcol; col <= curcolb; col++)
  2323.     {
  2324.       lstrcpy (str, "");
  2325.       if (cell [col] [row] != NULL)
  2326.       {
  2327.         cellptr = cell [col] [row];
  2328.         switch (cellptr->attrib)
  2329.         {
  2330.           case TEXTCELL:
  2331.             lstrcpy (str, cellptr->v.text);
  2332.           break;
  2333.  
  2334.           case VALUE:
  2335.             sprintf (str, "%1.*f", format [col] [row] & 
  2336.               15, cellptr->v.value);
  2337.             fsize = fsize + lstrlen (str);
  2338.           break;
  2339.           
  2340.           case FORMULA:
  2341.             if (formdisplay)
  2342.             {
  2343.               lstrcpy (str, "=");
  2344.               fixformula2 (col, row, -curcol, -currow);
  2345.               addformula ();
  2346.               fixformula2 (col, row, curcol, currow);
  2347.             }
  2348.             else
  2349.               sprintf (str, "%1.*f", format [col] [row] &
  2350.                 15, cellptr->v.value);
  2351.           break;
  2352.         }
  2353.       }
  2354.       lstrcat (ftext, str);
  2355.       str [0] = 9;
  2356.       str [1] = 0;
  2357.       if (col != curcolb) lstrcat (ftext, str);
  2358.     }
  2359.     str [1] = 0;
  2360.     str [0] = 13;
  2361.     lstrcat (ftext, str);
  2362.     str [0] = 10;
  2363.     lstrcat (ftext, str);
  2364.   }
  2365.   OemToChar (ftext, ftext);
  2366.   return (TRUE);
  2367. }
  2368.  
  2369. VOID savesheet (VOID)
  2370. {
  2371.   ZeroMemory (&ofn, sizeof (ofn));
  2372.   ofn.lStructSize = sizeof (ofn);
  2373.   ofn.hwndOwner = NULL;
  2374.   ofn.lpstrFile = filename;
  2375.   ofn.nMaxFile = MAX_PATH;
  2376.   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | 
  2377.     OFN_OVERWRITEPROMPT | OFN_ENABLEHOOK;
  2378.   ofn.lpstrFilter = "Tab Separated "
  2379.     "(*.tsv)\0*.tsv\0Text Files (*.txt)\0*.txt\0\0\0";
  2380.   ofn.lpstrDefExt = "tsv";
  2381.   ofn.lpfnHook = &hookproc2;
  2382.   n = GetSaveFileName (&ofn);
  2383.   SetActiveWindow (hwndmain);
  2384.   if (!n) return;
  2385.   writef (1, 25, PROMPTCOLOR, 79, MSGSAVING);
  2386.   SetConsoleCursorPosition (so, 
  2387.     (COORD) {lstrlen (MSGSAVING), 24});
  2388.   hfile = CreateFile (filename, GENERIC_WRITE, 0, 0,
  2389.      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  2390.   if (hfile == INVALID_HANDLE_VALUE)
  2391.   {
  2392.     errormsg (MSGNOOPEN);
  2393.     return;
  2394.   }
  2395.   col1 = curcol;
  2396.   row1 = currow;
  2397.   col2 = curcolb;
  2398.   row2 = currowb;
  2399.   ofd = formdisplay;
  2400.   formdisplay = 1;
  2401.   curcol = currow = 0;
  2402.   curcolb = lastcol;
  2403.   currowb = lastrow;
  2404.   if (!copys ())
  2405.   {
  2406.     curcol = col1;
  2407.     currow = row1;
  2408.     curcolb = col2;
  2409.     currowb = row2;
  2410.     formdisplay = ofd;
  2411.     errormsg (MSGLOMEM);
  2412.     return;
  2413.   }
  2414.   if (!WriteFile (hfile, ftext, lstrlen (ftext), 
  2415.       &written, NULL))
  2416.   {
  2417.     errormsg (MSGIOERROR);
  2418.     CloseHandle (hfile);
  2419.     return;
  2420.   }
  2421.   GlobalFree (ctext);
  2422.   if (!CloseHandle (hfile))
  2423.   {
  2424.     errormsg (MSGIOERROR);
  2425.     return;
  2426.   }
  2427.   writef (1, 25, TEXTCOLOR, lstrlen (MSGSAVING), "");
  2428.   SetConsoleCursorPosition (so, (COORD) {0, 24});
  2429.   curcol = col1;
  2430.   currow = row1;
  2431.   curcolb = col2;
  2432.   currowb = row2;
  2433.   formdisplay = ofd;
  2434.   changed = FALSE;
  2435. }
  2436.  
  2437. VOID pastes (VOID)
  2438. {
  2439.   ftext = (LPSTR) GlobalLock (ctext);
  2440.   CharToOem (ftext, ftext);
  2441.   ignorep = 0;
  2442.   col1 = curcol;
  2443.   row1 = currow;
  2444.   lstrcpy (str, "");
  2445.   loading = 1;
  2446.   for (ii = 0; ftext [ii] != 0; ii++)
  2447.   {
  2448.     for (jj = 0; jj <= 4; jj++) cstr [jj] = 0;
  2449.     for (jj = 0; jj <= 3 && 
  2450.       *(ftext + ii + jj) != 0; jj++)
  2451.       cstr [jj] = *(ftext + ii + jj);
  2452.     if (lstrcmpi (cstr, "sum(") == 0)
  2453.     {
  2454.       ii = ii + 3;
  2455.       ignorep = 1;
  2456.     }
  2457.     else if (ftext [ii] == 10)
  2458.     {
  2459.       if (lstrlen (str) != 0)
  2460.         act (str);
  2461.       else
  2462.         deletecell (curcol, currow, UPDATE);
  2463.       cellptr = cell [curcol] [currow];
  2464.       if (cellptr != NULL)
  2465.         if (cellptr->attrib == FORMULA)
  2466.           fixformula2 (curcol, currow, col1, row1);
  2467.       lstrcpy (str, "");
  2468.       curcol = col1;
  2469.      currow++;
  2470.     }
  2471.     else if (ftext [ii] == 9)
  2472.     {
  2473.       if (lstrlen (str) != 0)
  2474.         act (str);
  2475.       else
  2476.         deletecell (curcol, currow, UPDATE);
  2477.       cellptr = cell [curcol] [currow];
  2478.       if (cellptr != NULL)
  2479.         if (cellptr->attrib == FORMULA)
  2480.           fixformula2 (curcol, currow, col1, row1);
  2481.       lstrcpy (str, "");
  2482.       curcol++;
  2483.     }
  2484.     else if (ftext [ii] == ')' && ignorep == 1)
  2485.       ignorep = 0;
  2486.     else if (ftext [ii] != 13 && ftext [ii] != '=')
  2487.     {
  2488.       cstr [1] = 0;
  2489.       cstr [0] = ftext [ii];
  2490.       if (lstrlen (str) < MAXINPUT) lstrcat (str, cstr);
  2491.     }
  2492.   }
  2493.   curcol = col1;
  2494.   currow = row1;
  2495.   loading = 0;
  2496.   if (!formdisplay && autocalc) recalc ();
  2497. }
  2498.  
  2499. VOID loadsheet (BOOL comline)
  2500. {
  2501.   reallastcol = reallastrow = 0;
  2502.   success = FALSE;
  2503.   if (!comline)
  2504.   {
  2505.     filename [0] = 0;
  2506.     ZeroMemory (&ofn, sizeof (ofn));
  2507.     ofn.lStructSize = sizeof (ofn);
  2508.     ofn.hwndOwner = NULL;
  2509.     ofn.lpstrFile = filename;
  2510.     ofn.nMaxFile = MAX_PATH;
  2511.     ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST |
  2512.       OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | 
  2513.       OFN_ENABLEHOOK;
  2514.     ofn.lpstrFilter = "Tab Separated "
  2515.       "(*.tsv)\0*.tsv\0Text Files (*.txt)\0*.txt\0\0\0";
  2516.     ofn.lpstrDefExt = "tsv";
  2517.     ofn.lpfnHook = &hookproc2;
  2518.     n = GetOpenFileName (&ofn);
  2519.     SetActiveWindow (hwndmain);
  2520.     if (!n) return;
  2521.   }
  2522.   hfile = CreateFile (filename, GENERIC_READ,
  2523.     FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
  2524.   if (hfile == INVALID_HANDLE_VALUE)
  2525.   {
  2526.     errormsg (MSGNOOPEN);
  2527.     return;
  2528.   }
  2529.   clearsheet ();
  2530.   writef (1, 25, PROMPTCOLOR, 79, MSGLOADING);
  2531.   SetConsoleCursorPosition (so, 
  2532.     (COORD) {lstrlen (MSGLOADING), 24});
  2533.   fsize = GetFileSize (hfile, NULL);
  2534.   if (fsize != 0xFFFFFFFF)
  2535.   {
  2536.     ctext = GlobalAlloc (GMEM_MOVEABLE | 
  2537.       GMEM_DDESHARE, fsize + 1);
  2538.     if (ctext != NULL)
  2539.     {
  2540.       ftext = (LPSTR) GlobalLock (ctext);
  2541.       if (ReadFile (hfile, ftext, fsize, 
  2542.         &written, NULL))
  2543.       {
  2544.          ftext [fsize] = 0;
  2545.          success = TRUE;
  2546.       }
  2547.     }
  2548.   }
  2549.   if (success == FALSE)
  2550.   {
  2551.     errormsg (MSGIOERROR);
  2552.     return;
  2553.   }
  2554.   if (!CloseHandle (hfile))
  2555.   {
  2556.     errormsg (MSGIOERROR);
  2557.     return;
  2558.   }
  2559.   loading = 1;
  2560.   pastes ();
  2561.   loading = 0;
  2562.   displayscreen (NOUPDATE);
  2563.   writef (1, 25, TEXTCOLOR, lstrlen (MSGLOADING), "");
  2564.   SetConsoleCursorPosition (so, (COORD) {0, 24});
  2565.   printfreemem ();
  2566.   GlobalFree (ctext);
  2567.   curcol = currow = curcolb = currowb = 0;
  2568.   setrightcol ();
  2569.   displayscreen (NOUPDATE);
  2570.   changed = FALSE;
  2571. }
  2572.  
  2573. VOID initvars (VOID)
  2574. {
  2575.   leftcol = toprow = curcol = currow = lastcol = 
  2576.     lastrow = curcolb = currowb = 0;
  2577.   FillMemory (colwidth, sizeof (colwidth), DEFAULTWIDTH);
  2578.   FillMemory (cell, sizeof (cell), 0);
  2579.   FillMemory (format, sizeof (format), DEFAULTFORMAT);
  2580. }
  2581.  
  2582. VOID clearsheet (VOID)
  2583. {
  2584.   for (row = 0; row <= lastrow; row++)
  2585.     for (col = 0; col <= lastcol; col++)
  2586.       deletecell (col, row, NOUPDATE);
  2587.   initvars ();
  2588.   setrightcol ();
  2589.   setbottomrow ();
  2590.   curcolb = curcol;
  2591.   currowb = currow;
  2592.   displayscreen (NOUPDATE);
  2593.   printfreemem ();
  2594.   changed = FALSE;
  2595. }
  2596.  
  2597. VOID editcell (CELLPTR ecell)
  2598. {
  2599.   if (ecell == NULL) return;
  2600.   switch (ecell->attrib)
  2601.   {
  2602.     case TEXTCELL: lstrcpy (str, ecell->v.text); break;
  2603.     
  2604.     case VALUE:
  2605.       if (ecell->v.value == HUGE_VAL)
  2606.         lstrcpy (str, "0");
  2607.      else
  2608.        sprintf (str, "%.*f", MAXPLACES, ecell->v.value);
  2609.     break;
  2610.     
  2611.     case FORMULA: 
  2612.       lstrcpy (str, ecell->v.f.formula);
  2613.     break;
  2614.   }
  2615.   if (!editstring (str, "", MAXINPUT) || (str [0] == 0)) 
  2616.     return;
  2617.   act (str);
  2618.   changed = TRUE;
  2619. }
  2620.  
  2621. VOID changeformdisplay (INT newmode)
  2622. {
  2623.   formdisplay = newmode;
  2624.   if (formdisplay)
  2625.     strcpy (str, MSGFORMDISPLAY);
  2626.   else
  2627.     str [0] = 0;
  2628.   writef (65, 1, MSGFORMDISPLAYCOLOR, 
  2629.     lstrlen (MSGFORMDISPLAY), str);
  2630.   if (!formdisplay && autocalc) recalc ();
  2631. }
  2632.  
  2633. VOID changeautocalc (INT newmode)
  2634. {
  2635.   if (!autocalc && newmode) recalc ();
  2636.   autocalc = newmode;
  2637.   if (autocalc)
  2638.     strcpy (str, MSGAUTOCALC);
  2639.   else
  2640.     str [0] = 0;
  2641.   writef (73, 1, MSGAUTOCALCCOLOR, lstrlen (MSGAUTOCALC),
  2642.     str);
  2643. }
  2644.  
  2645. VOID recalc (VOID)
  2646. {
  2647.   for (col = 0; col <= lastcol; col++)
  2648.     for (row = 0; row <= lastrow; row++)
  2649.       if ((cell [col] [row] != NULL) && 
  2650.         (cell [col] [row]->attrib == FORMULA))
  2651.         cell [col] [row]->v.f.fvalue = 
  2652.           parse (cell [col] [row]->v.f.formula, &dummy);
  2653.   displayscreen (UPDATE);
  2654. }
  2655.  
  2656. VOID movecolright(VOID)
  2657. {
  2658.   MoveMemory (oldcolstart, colstart, sizeof (colstart));
  2659.   oldleftcol = leftcol;
  2660.   oldrightcol = rightcol;
  2661.   if (curcolb == curcol)
  2662.     displaycell (curcol, currow, NOHIGHLIGHT, NOUPDATE);
  2663.   if (curcol < rightcol && curcolb < rightcol)
  2664.   {
  2665.     if (curcolb == curcol && currowb == currow)
  2666.     {
  2667.       curcol++;
  2668.       curcolb = curcol;
  2669.     }
  2670.   }
  2671.   else 
  2672.     if (rightcol < (MAXCOLS - 1))
  2673.     {
  2674.       if (currowb == currow && curcolb == curcol)
  2675.       {
  2676.         curcol++;
  2677.         curcolb = curcol;
  2678.       }
  2679.       rightcol++;
  2680.       setleftcol ();
  2681.       setrightcol ();
  2682.       if (oldrightcol >= leftcol)
  2683.         scroll (LEFT, 
  2684.           oldcolstart [leftcol - oldleftcol] - 
  2685.           LEFTMARGIN, LEFTMARGIN + 1, 3, 80, 
  2686.           SCREENROWS + 2, TEXTCOLOR);
  2687.       clearlastcol ();
  2688.       for (col = oldrightcol + 1; col <= rightcol; col++)
  2689.         displaycol (col, NOUPDATE);
  2690.     }
  2691. }
  2692.  
  2693. VOID movecolleft (VOID)
  2694. {
  2695.   oldleftcol = leftcol;
  2696.   MoveMemory (oldcolstart, colstart, sizeof (colstart));
  2697.   if (curcolb == curcol)
  2698.     displaycell (curcol, currow, NOHIGHLIGHT, NOUPDATE);
  2699.   if (curcol > leftcol && curcolb > leftcol)
  2700.   { 
  2701.     if (curcolb == curcol && currowb == currow)
  2702.     {
  2703.       curcol--;
  2704.       curcolb = curcol;
  2705.     }
  2706.   }
  2707.   else
  2708.     if (leftcol != 0)
  2709.     {
  2710.       if (currowb == currow && curcolb == curcol)
  2711.       {
  2712.         curcol--;
  2713.         curcolb = curcol;
  2714.       }
  2715.       leftcol--;
  2716.       setrightcol ();
  2717.       setleftcol ();
  2718.       if (oldleftcol <= rightcol)
  2719.         scroll (RIGHT, colstart[oldleftcol - leftcol] - 
  2720.           LEFTMARGIN, LEFTMARGIN + 1, 3, 80, 
  2721.           SCREENROWS + 2, TEXTCOLOR);
  2722.       clearlastcol ();
  2723.       for (col = leftcol; col <= oldleftcol - 1; col++)
  2724.         displaycol (col, NOUPDATE);
  2725.     }
  2726. }
  2727.  
  2728. VOID moverowdown (VOID)
  2729. {
  2730.   if (currowb == currow)
  2731.     displaycell (curcol, currow, NOHIGHLIGHT, NOUPDATE);
  2732.   if (currow < bottomrow && currowb < bottomrow)
  2733.   {
  2734.     if (currowb == currow && curcolb == curcol)
  2735.     {
  2736.       currow++;
  2737.       currowb = currow;
  2738.     }
  2739.   }
  2740.   else 
  2741.     if (bottomrow < MAXROWS - 1)
  2742.     {
  2743.       scroll (UP, 1, LEFTMARGIN + 1, 3, 80, 
  2744.         SCREENROWS + 2, TEXTCOLOR);
  2745.       toprow++;
  2746.       if (currowb == currow && curcolb == curcol)
  2747.       {
  2748.         currow++;
  2749.         currowb = currow;
  2750.       }
  2751.       setbottomrow ();
  2752.       displayrow (bottomrow, NOUPDATE);
  2753.     }
  2754. }
  2755.  
  2756. VOID moverowup (VOID)
  2757. {
  2758.   if (currowb == currow)
  2759.     displaycell (curcol, currow, NOHIGHLIGHT, NOUPDATE);
  2760.   if (currow > toprow && currowb > toprow)
  2761.   {
  2762.     if (currowb == currow && curcolb == curcol)
  2763.     {
  2764.       currow--;
  2765.       currowb = currow;
  2766.     }
  2767.   }
  2768.   else
  2769.     if (toprow != 0)
  2770.     {
  2771.       scroll (DOWN, 1, LEFTMARGIN + 1, 3, 80, 
  2772.         SCREENROWS + 2, TEXTCOLOR);
  2773.       displayrow (--toprow, NOUPDATE);
  2774.       if (currowb == currow && curcolb == curcol)
  2775.       {
  2776.         currow--;
  2777.         currowb = currow;
  2778.       }
  2779.       setbottomrow ();
  2780.     }
  2781. }
  2782.  
  2783. //reads in a command and acts on it
  2784. INT getcommand (LPSTR msgstr, LPSTR comstr)
  2785. {
  2786.   len = lstrlen (msgstr);
  2787.   scroll (UP, 0, 1, 24, 80, 24, TEXTCOLOR);
  2788.   for (counter = 0; counter < len; counter++)
  2789.   {
  2790.     if (IsCharUpper (msgstr [counter]))
  2791.       writef (counter + 1, 24, COMMANDCOLOR, 1, "%c", 
  2792.         msgstr [counter]);
  2793.     else
  2794.       writef (counter + 1, 24, LOWCOMMANDCOLOR, 1, "%c",
  2795.         msgstr [counter]);
  2796.   }
  2797.   do
  2798.     ch = (CHAR) CharUpper ((LPSTR) getkey ());
  2799.   while (strchr (comstr, ch) == NULL && ch != ESC);
  2800.   scroll (UP, 0, 1, 25, 80, 25, TEXTCOLOR);
  2801.   SetConsoleCursorPosition (so, (COORD) {0, 24});
  2802.   if (ch == ESC) return (-1);
  2803.   return (lstrlen (comstr) - 
  2804.     lstrlen (strchr (comstr, ch)));
  2805. }
  2806.  
  2807. VOID mainmenu (VOID)
  2808. {
  2809.   switch (getcommand (MENU, COMMAND))
  2810.   {
  2811.     case 0: 
  2812.       switch (getcommand (SMENU, SCOMMAND))
  2813.       {
  2814.         case 0:
  2815.           if (changed && getyesno (&i, 
  2816.             MSGSAVESHEET) && i == 'Y')
  2817.             savesheet ();
  2818.           loadsheet (FALSE);
  2819.         break;
  2820.     
  2821.         case 1: savesheet (); break;
  2822.         case 2: printsheet (); break;
  2823.     
  2824.         case 3:
  2825.           if (changed && getyesno (&i, 
  2826.             MSGSAVESHEET) && i == 'Y')
  2827.             savesheet ();
  2828.           clearsheet ();
  2829.         break;
  2830.     
  2831.         case 4:
  2832.           lstrcpy (str, MSGABOUT1);
  2833.           lstrcat (str, MSGABOUT2);
  2834.           MessageBox (NULL, str, 
  2835.             "About this program", MB_SETFOREGROUND);
  2836.         break;
  2837.       }
  2838.     break;
  2839.     
  2840.     case 1: formatcells (); break;
  2841.     
  2842.     case 2:
  2843.       deletecell (curcol, currow, UPDATE);
  2844.       printfreemem ();
  2845.       if (autocalc) recalc ();
  2846.     break;
  2847.     
  2848.     case 3:
  2849.       if (curcolb == curcol && currowb == currow)
  2850.       {
  2851.         writef (1, 24, PROMPTCOLOR, 80, MSGGOTO);
  2852.         if (getcell (&curcol, &currow))
  2853.         {
  2854.           leftcol = curcol;
  2855.           toprow = currow;
  2856.           setbottomrow ();
  2857.           setrightcol ();
  2858.           setleftcol ();
  2859.           displayscreen (NOUPDATE);
  2860.         }
  2861.       }
  2862.     break;
  2863.  
  2864.     case 4: 
  2865.       if (curcolb == curcol && currowb == currow) 
  2866.         switch (getcommand (CMENU, CCOMMAND))
  2867.         {
  2868.           case 0: insertcol (curcol); break;
  2869.           case 1: deletecol (curcol); break;
  2870.           case 2: setcolwidth (curcol); break;
  2871.         }
  2872.     break;
  2873.     
  2874.     case 5: 
  2875.       if (curcolb == curcol && currowb == currow) 
  2876.         switch (getcommand (RMENU, RCOMMAND))
  2877.         {
  2878.           case 0: insertrow (currow); break;
  2879.           case 1: deleterow (currow); break;
  2880.         }
  2881.     break;
  2882.     
  2883.     case 6: editcell (curcell); break;
  2884.     
  2885.     case 7: 
  2886.       switch (getcommand (UMENU, UCOMMAND))
  2887.       {
  2888.         case 0: recalc (); break;
  2889.     
  2890.         case 1:
  2891.           changeformdisplay (!formdisplay);
  2892.           displayscreen (UPDATE);
  2893.         break;
  2894.       }
  2895.     break;
  2896.     
  2897.     case 8: changeautocalc (!autocalc);  break;
  2898.     
  2899.     case 9:
  2900.       if (changed && getyesno (&i, 
  2901.         MSGSAVESHEET) && i == 'Y')
  2902.         savesheet ();
  2903.       stop = TRUE;
  2904.     break;
  2905.   }
  2906. }
  2907.  
  2908. UINT APIENTRY hookproc1 (HWND hdlg, UINT uimsg, 
  2909.   WPARAM wparam, LPARAM lparam)
  2910. {
  2911.   ShowWindow (hdlg, 0);
  2912.   PostMessage (hdlg, WM_COMMAND, IDABORT, 0);
  2913.   EndDialog (hdlg, 1);
  2914.   hwndmain = GetParent (hdlg);
  2915.   SetForegroundWindow (hwndmain);
  2916.   return 0;
  2917. }
  2918.  
  2919. VOID main (INT argc, LPSTR argv [])
  2920. {
  2921.   si = GetStdHandle (STD_INPUT_HANDLE);
  2922.   so = GetStdHandle (STD_OUTPUT_HANDLE);
  2923.   SetConsoleMode (si, ENABLE_WINDOW_INPUT);
  2924.   SetConsoleTitle (MSGTITLE);
  2925.   setcursor (shortcursor);
  2926.   setcursor (nocursor);
  2927.   changed = formdisplay = stop = FALSE;
  2928.   autocalc = TRUE;
  2929.   loading = coord.X = 0;
  2930.   for (coord.Y = 0; coord.Y <= 24; coord.Y++)
  2931.     FillConsoleOutputCharacter 
  2932.       (so, ' ', 80, coord, &written);
  2933.   for (coord.Y = 0; coord.Y <= 79; coord.Y++)
  2934.     FillConsoleOutputAttribute 
  2935.       (so, TEXTCOLOR, 80, coord, &written);
  2936.   ZeroMemory (&ofn, sizeof (ofn));
  2937.   ofn.lStructSize = sizeof (ofn);
  2938.   ofn.hwndOwner = NULL;
  2939.   ofn.lpstrFile = filename;
  2940.   ofn.nMaxFile = MAX_PATH;
  2941.   ofn.Flags = OFN_ENABLEHOOK;
  2942.   ofn.lpstrFilter = "0\0\0\0\0";
  2943.   ofn.lpstrDefExt = "";
  2944.   ofn.lpfnHook = &hookproc1;
  2945.   GetOpenFileName(&ofn);
  2946.   initvars ();
  2947.   setrightcol ();
  2948.   setbottomrow ();
  2949.   writef (1, 1, MSGMEMORYCOLOR, lstrlen (MSGMEMORY), 
  2950.     MSGMEMORY);
  2951.   writef (29, 1, PROMPTCOLOR, lstrlen (MSGCOMMAND), 
  2952.     MSGCOMMAND);
  2953.   changeautocalc (autocalc);
  2954.   changeformdisplay (formdisplay);
  2955.   printfreemem ();
  2956.   displayscreen (NOUPDATE);
  2957.   if (argc > 1)
  2958.   {
  2959.     lstrcpy (filename, argv [1]);
  2960.     for (i = 0; filename [i] != '.'; i++);
  2961.     for (j = 0; j <= 2; j++) 
  2962.       str [j] = filename [i + j + 1];
  2963.     str [3] = 0;
  2964.     if (lstrcmpi (str, "tsv") == 0 || 
  2965.       lstrcmpi (str, "txt") == 0) 
  2966.       loadsheet (TRUE);
  2967.   }
  2968.   scroll (UP, 0, 1, 25, 80, 25, TEXTCOLOR);
  2969.   SetConsoleCursorPosition (so, (COORD) {0, 24});
  2970.   do
  2971.   {
  2972.     if (curcol == curcolb && currow == currowb)
  2973.       displaycell (curcol, currow, HIGHLIGHT, NOUPDATE);
  2974.     else
  2975.       displayscreen (NOUPDATE);
  2976.     curcell = cell [curcol] [currow];
  2977.     showcelltype ();
  2978.     inpt = getkey ();
  2979.     switch (inpt)
  2980.     {
  2981.       case '/': mainmenu (); break;
  2982.       case F9: recalc (); break;
  2983.       case F2: editcell (curcell); break;
  2984.       
  2985.       case SHIFTF9:
  2986.         changeformdisplay (!formdisplay);
  2987.         displayscreen (UPDATE);
  2988.       break;
  2989.      
  2990.       case DELKEY:
  2991.         for (ii = currow; ii <= currowb; ii++)
  2992.           for (jj = curcol; jj <= curcolb; jj++)
  2993.             deletecell (jj, ii, UPDATE);
  2994.         printfreemem ();
  2995.         if (autocalc) recalc ();
  2996.       break;
  2997.      
  2998.       case PGUPKEY:
  2999.         if (curcolb == curcol && currowb == currow)
  3000.         {
  3001.           toprow -= 20;
  3002.           currow -= 20;
  3003.           if (currow < 0)
  3004.             currow = toprow = 0;
  3005.           else
  3006.             if (toprow < 0)
  3007.             {
  3008.               currow -= toprow;
  3009.               toprow = 0;
  3010.             }
  3011.           currowb = currow;
  3012.           setbottomrow ();
  3013.           displayscreen (NOUPDATE);
  3014.         }
  3015.       break;
  3016.      
  3017.       case PGDNKEY:
  3018.         if (curcolb == curcol && currowb == currow)
  3019.         {
  3020.           toprow += 20;
  3021.           currow += 20;
  3022.           if ((currow >= MAXROWS) && (toprow >= MAXROWS))
  3023.           {
  3024.             currow = MAXROWS - 1;
  3025.             toprow = MAXROWS - 20;
  3026.           }
  3027.           else
  3028.             if (toprow > (MAXROWS - 20))
  3029.             {
  3030.               currow -= (toprow + 20 - MAXROWS);
  3031.               toprow = MAXROWS - 20;
  3032.             }
  3033.           currowb = currow;
  3034.           setbottomrow ();
  3035.           displayscreen (NOUPDATE);
  3036.         }
  3037.       break;
  3038.      
  3039.       case CTRLLEFTKEY:
  3040.         if (curcolb == curcol && currowb == currow)
  3041.         {
  3042.           displaycell (curcol, currow, NOHIGHLIGHT, 
  3043.             NOUPDATE);
  3044.           if (leftcol == 0)
  3045.           {
  3046.             curcol = 0;
  3047.             curcolb = curcol;
  3048.           }
  3049.           else
  3050.           {
  3051.             curcol = rightcol = leftcol - 1;
  3052.             curcolb = curcol;
  3053.             setleftcol ();
  3054.             setrightcol ();
  3055.             displayscreen (NOUPDATE);
  3056.           }
  3057.         }
  3058.       break;
  3059.      
  3060.       case CTRLRIGHTKEY:
  3061.         if (curcolb == curcol && currowb == currow)
  3062.         {
  3063.           displaycell (curcol, currow, NOHIGHLIGHT, 
  3064.             NOUPDATE);
  3065.           if (rightcol == MAXCOLS - 1)
  3066.           {
  3067.             curcol = rightcol;
  3068.             curcolb = curcol;
  3069.           }
  3070.           else
  3071.           {
  3072.             curcol = leftcol = rightcol + 1;
  3073.             curcolb = curcol;
  3074.             setrightcol ();
  3075.             setleftcol ();
  3076.             displayscreen (NOUPDATE);
  3077.           }
  3078.         }
  3079.       break;
  3080.      
  3081.       case HOMEKEY:
  3082.         if (curcolb == curcol && currowb == currow)
  3083.         {
  3084.           currow = curcol = leftcol = toprow = 0;
  3085.           currowb = currow;
  3086.           curcolb = curcol;
  3087.           setrightcol ();
  3088.           setbottomrow ();
  3089.           displayscreen (NOUPDATE);
  3090.         }
  3091.       break;
  3092.      
  3093.       case ENDKEY:
  3094.         if (curcolb == curcol && currowb == currow)
  3095.         {
  3096.           rightcol = curcol = lastcol;
  3097.           currow = bottomrow = lastrow;
  3098.           currowb = currow;
  3099.           curcolb = curcol;
  3100.           if (bottomrow - SCREENROWS < -1) 
  3101.             bottomrow = 19;
  3102.           toprow = bottomrow - 19;
  3103.           for (row = 0; row < SCREENROWS; row++)
  3104.             writef (1, row + 3, HEADERCOLOR, LEFTMARGIN, "%-d", 
  3105.               row + toprow + 1);
  3106.           setleftcol ();
  3107.           setrightcol ();
  3108.           displayscreen (NOUPDATE);
  3109.         }
  3110.       break;
  3111.  
  3112.       case SHIFTENDKEY:
  3113.         rightcol = lastcol;
  3114.         bottomrow = lastrow;
  3115.         currowb = lastrow;
  3116.         curcolb = lastcol;
  3117.         if (bottomrow - SCREENROWS < -1) 
  3118.           bottomrow = 19;
  3119.         toprow = bottomrow - 19;
  3120.         for (row = 0; row < SCREENROWS; row++)
  3121.           writef (1, row + 3, HEADERCOLOR, LEFTMARGIN, "%-d", 
  3122.             row + toprow + 1);
  3123.         setbottomrow ();
  3124.         setleftcol ();
  3125.         setrightcol ();
  3126.         displayscreen (NOUPDATE);
  3127.       break;
  3128.      
  3129.       case UPKEY:
  3130.         if (curcolb != curcol || currowb != currow)
  3131.         { 
  3132.           curcolb = curcol;
  3133.           currowb = currow;
  3134.           leftcol = leftcolb;
  3135.           toprow = toprowb;
  3136.           setbottomrow ();
  3137.           setrightcol ();
  3138.           setleftcol ();
  3139.           displayscreen (NOUPDATE);
  3140.         }
  3141.         else
  3142.           moverowup ();
  3143.       break;
  3144.       
  3145.       case DOWNKEY: 
  3146.         if (curcolb != curcol || currowb != currow)
  3147.         { 
  3148.           curcolb = curcol;
  3149.           currowb = currow;
  3150.           leftcol = leftcolb;
  3151.           toprow = toprowb;
  3152.           setbottomrow ();
  3153.           setrightcol ();
  3154.           setleftcol ();
  3155.           displayscreen (NOUPDATE);
  3156.         }
  3157.         else
  3158.           moverowdown (); 
  3159.       break;
  3160.       
  3161.       case LEFTKEY: 
  3162.         if (curcolb != curcol || currowb != currow)
  3163.         { 
  3164.           curcolb = curcol;
  3165.           currowb = currow;
  3166.           leftcol = leftcolb;
  3167.           toprow = toprowb;
  3168.           setbottomrow ();
  3169.           setrightcol ();
  3170.           setleftcol ();
  3171.           displayscreen (NOUPDATE);
  3172.         }
  3173.         else
  3174.           movecolleft (); 
  3175.       break;
  3176.       
  3177.       case RIGHTKEY:
  3178.         if (curcolb != curcol || currowb != currow)
  3179.         { 
  3180.           curcolb = curcol;
  3181.           currowb = currow;
  3182.           leftcol = leftcolb;
  3183.           toprow = toprowb;
  3184.           setbottomrow ();
  3185.           setrightcol ();
  3186.           setleftcol ();
  3187.           displayscreen (NOUPDATE);
  3188.         }
  3189.         else
  3190.           movecolright ();
  3191.       break;
  3192.       
  3193.       case SHIFTUPKEY:
  3194.         if (currow <= currowb - 1)
  3195.         {
  3196.           currowb--;
  3197.           if (curcol == curcolb && currow == currowb)
  3198.             displayscreen (NOUPDATE);
  3199.           else
  3200.             moverowup ();
  3201.         }
  3202.       break;
  3203.       
  3204.       case SHIFTDOWNKEY:
  3205.         if (currowb == currow && curcolb == curcol)
  3206.         {
  3207.           leftcolb = leftcol;
  3208.           toprowb = toprow;
  3209.         }
  3210.         if (MAXROWS >= currowb + 1)
  3211.         { 
  3212.           currowb++;
  3213.           moverowdown ();
  3214.         }
  3215.       break;
  3216.       
  3217.       case SHIFTLEFTKEY:
  3218.         if (curcol <= curcolb - 1)
  3219.         {
  3220.           curcolb--;
  3221.           if (curcol == curcolb && currow == currowb)
  3222.             displayscreen (NOUPDATE);
  3223.           else
  3224.             movecolleft ();
  3225.         }
  3226.       break;
  3227.       
  3228.       case SHIFTRIGHTKEY:
  3229.         if (currowb == currow && curcolb == curcol)
  3230.         {
  3231.           leftcolb = leftcol;
  3232.           toprowb = toprow;
  3233.         }
  3234.         if (MAXROWS >= curcolb + 1)
  3235.         { 
  3236.           curcolb++;
  3237.           movecolright ();
  3238.         }
  3239.       break;
  3240.       
  3241.       case CTRLC:
  3242.         if (!copys ())
  3243.         {
  3244.           errormsg (MSGLOMEM);
  3245.           break;
  3246.         }
  3247.         GlobalUnlock (ctext);
  3248.         if (!OpenClipboard (NULL) ||
  3249.           !EmptyClipboard () || 
  3250.           SetClipboardData (CF_TEXT, ctext) == NULL)
  3251.         {
  3252.           CloseClipboard ();
  3253.           GlobalFree (ctext);
  3254.           break;
  3255.         }
  3256.         CloseClipboard ();
  3257.       break;
  3258.       
  3259.       case CTRLV:
  3260.         if (!OpenClipboard (NULL) ||
  3261.           (ctext = GetClipboardData (CF_TEXT)) == NULL)
  3262.         {
  3263.           CloseClipboard ();
  3264.           break;
  3265.         }
  3266.         pastes ();
  3267.         GlobalUnlock (ctext);
  3268.         CloseClipboard ();
  3269.         changed = TRUE;
  3270.         displayscreen (NOUPDATE);
  3271.       break;
  3272.       
  3273.       case ALTF4:
  3274.         if (changed && getyesno (&i, 
  3275.           MSGSAVESHEET) && i == 'Y')
  3276.           savesheet ();
  3277.         stop = TRUE;
  3278.       break;
  3279.       
  3280.       case ESC: case BS: case CR: case INSKEY: 
  3281.       case TABKEY: case F4:
  3282.       break;
  3283.       
  3284.       default:
  3285.           stri [0] = inpt;
  3286.           stri [1] = 0;
  3287.           if (editstring (stri, "", MAXINPUT) &&
  3288.             stri [0] != 0)
  3289.           {
  3290.             act (stri);
  3291.             changed = TRUE;
  3292.           }
  3293.       break;
  3294.     }
  3295.   } 
  3296.   while (!stop);
  3297. }
  3298.  
  3299.